@ctrl/plex 1.5.3 → 2.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.
Files changed (68) hide show
  1. package/README.md +3 -2
  2. package/dist/src/alert.d.ts +12 -0
  3. package/dist/src/alert.js +29 -0
  4. package/dist/src/alert.types.d.ts +59 -0
  5. package/dist/src/alert.types.js +1 -0
  6. package/dist/{base → src/base}/partialPlexObject.d.ts +18 -12
  7. package/dist/{base → src/base}/partialPlexObject.js +29 -23
  8. package/dist/{base → src/base}/playable.d.ts +2 -2
  9. package/dist/src/base/playable.js +8 -0
  10. package/dist/{base → src/base}/plexObject.d.ts +8 -1
  11. package/dist/{base → src/base}/plexObject.js +21 -18
  12. package/dist/{baseFunctionality.d.ts → src/baseFunctionality.d.ts} +17 -1
  13. package/dist/{baseFunctionality.js → src/baseFunctionality.js} +7 -15
  14. package/dist/{client.d.ts → src/client.d.ts} +2 -2
  15. package/dist/{client.js → src/client.js} +12 -20
  16. package/dist/src/client.types.js +1 -0
  17. package/dist/src/config.js +35 -0
  18. package/dist/src/exceptions.d.ts +20 -0
  19. package/dist/src/exceptions.js +40 -0
  20. package/dist/src/index.d.ts +12 -0
  21. package/dist/src/index.js +11 -0
  22. package/dist/{library.d.ts → src/library.d.ts} +207 -21
  23. package/dist/{library.js → src/library.js} +348 -132
  24. package/dist/{library.types.d.ts → src/library.types.d.ts} +59 -1
  25. package/dist/src/library.types.js +1 -0
  26. package/dist/{media.d.ts → src/media.d.ts} +16 -4
  27. package/dist/{media.js → src/media.js} +42 -49
  28. package/dist/src/media.types.d.ts +7 -0
  29. package/dist/src/media.types.js +1 -0
  30. package/dist/{myplex.d.ts → src/myplex.d.ts} +16 -6
  31. package/dist/{myplex.js → src/myplex.js} +71 -57
  32. package/dist/src/myplex.types.js +10 -0
  33. package/dist/src/playlist.d.ts +75 -0
  34. package/dist/src/playlist.js +142 -0
  35. package/dist/src/playlist.types.d.ts +17 -0
  36. package/dist/src/playlist.types.js +1 -0
  37. package/dist/{search.d.ts → src/search.d.ts} +4 -3
  38. package/dist/{search.js → src/search.js} +13 -19
  39. package/dist/src/search.types.js +1 -0
  40. package/dist/{server.d.ts → src/server.d.ts} +22 -10
  41. package/dist/{server.js → src/server.js} +65 -50
  42. package/dist/src/server.types.js +1 -0
  43. package/dist/src/settings.d.ts +79 -0
  44. package/dist/src/settings.js +160 -0
  45. package/dist/{util.d.ts → src/util.d.ts} +2 -1
  46. package/dist/{util.js → src/util.js} +8 -12
  47. package/dist/{video.d.ts → src/video.d.ts} +38 -60
  48. package/dist/{video.js → src/video.js} +109 -92
  49. package/dist/{video.types.d.ts → src/video.types.d.ts} +1 -1
  50. package/dist/src/video.types.js +6 -0
  51. package/package.json +46 -44
  52. package/dist/base/playable.js +0 -12
  53. package/dist/client.types.js +0 -2
  54. package/dist/config.js +0 -41
  55. package/dist/index.d.ts +0 -8
  56. package/dist/index.js +0 -23
  57. package/dist/library.types.js +0 -2
  58. package/dist/myplex.types.js +0 -13
  59. package/dist/playlist.d.ts +0 -7
  60. package/dist/playlist.js +0 -19
  61. package/dist/search.types.js +0 -2
  62. package/dist/server.types.js +0 -2
  63. package/dist/video.types.js +0 -9
  64. /package/dist/{client.types.d.ts → src/client.types.d.ts} +0 -0
  65. /package/dist/{config.d.ts → src/config.d.ts} +0 -0
  66. /package/dist/{myplex.types.d.ts → src/myplex.types.d.ts} +0 -0
  67. /package/dist/{search.types.d.ts → src/search.types.d.ts} +0 -0
  68. /package/dist/{server.types.d.ts → src/server.types.d.ts} +0 -0
@@ -1,14 +1,13 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Collections = exports.Folder = exports.Hub = exports.ShowSection = exports.MovieSection = exports.LibrarySection = exports.Library = void 0;
4
- const url_1 = require("url");
5
- const plexObject_1 = require("./base/plexObject");
6
- const video_1 = require("./video");
7
- const baseFunctionality_1 = require("./baseFunctionality");
8
- const search_1 = require("./search");
9
- const playlist_1 = require("./playlist");
10
- const partialPlexObject_1 = require("./base/partialPlexObject");
11
- class Library {
1
+ import { URLSearchParams } from 'url';
2
+ import { PartialPlexObject } from './base/partialPlexObject.js';
3
+ import { PlexObject } from './base/plexObject.js';
4
+ import { fetchItem, fetchItems, findItems } from './baseFunctionality.js';
5
+ import { NotFound } from './exceptions.js';
6
+ import { Playlist } from './playlist.js';
7
+ import { searchType } from './search.js';
8
+ import { Movie, Show } from './video.js';
9
+ export class Library {
10
+ static { this.key = '/library'; }
12
11
  constructor(server, data) {
13
12
  this.server = server;
14
13
  this._loadData(data);
@@ -41,8 +40,9 @@ class Library {
41
40
  return section;
42
41
  }
43
42
  async sectionByID(sectionId) {
43
+ const sectionIdStr = sectionId.toString();
44
44
  const sections = await this.sections();
45
- const section = sections.find(s => s.key);
45
+ const section = sections.find(s => s.key === sectionIdStr);
46
46
  if (!section) {
47
47
  throw new Error(`Invalid library section id: ${sectionId}`);
48
48
  }
@@ -193,7 +193,7 @@ class Library {
193
193
  * 46:United Kingdom, 47:United States, 48:Uruguay, 49:Venezuela.
194
194
  */
195
195
  async add(name, type, agent, scanner, location, language = 'en', extra = {}) {
196
- const search = new url_1.URLSearchParams({
196
+ const search = new URLSearchParams({
197
197
  name,
198
198
  type,
199
199
  agent,
@@ -239,6 +239,30 @@ class Library {
239
239
  async optimize() {
240
240
  await this.server.query('/library/optimize?async=1', 'put');
241
241
  }
242
+ /**
243
+ * Validates a filter field and values are available as a custom filter for the library.
244
+ * Returns the validated field and values as a URL encoded parameter string.
245
+ */
246
+ // _validateFilterField(field: string): string {
247
+ // const match = /(?:([a-zA-Z]*)\.)?([a-zA-Z]+)([!<>=&]*)/.test(field);
248
+ // if (!match) {
249
+ // throw new Error('Invalid filter field: ' + field);
250
+ // }
251
+ // }
252
+ /**
253
+ * Returns the validated and formatted search query API key
254
+ * (``/library/sections/<sectionKey>/all?<params>``).
255
+ */
256
+ // _buildSearchKey(kwargs: Record<string, string>) {
257
+ // const args: Record<string, string> = {};
258
+ // const filterArgs = [];
259
+ // for (const [field, values] of Object.entries(kwargs)) {
260
+ // if (!(field.split('__')[-1] in OPERATORS)) {
261
+ // filterArgs.push(this._validateFilterField(field, values, libtype));
262
+ // delete kwargs[field];
263
+ // }
264
+ // }
265
+ // }
242
266
  _loadData(data) {
243
267
  this.identifier = data.identifier;
244
268
  this.mediaTagPrefix = data.mediaTagPrefix;
@@ -246,45 +270,95 @@ class Library {
246
270
  this.title2 = data.title2;
247
271
  }
248
272
  }
249
- exports.Library = Library;
250
- Library.key = '/library';
251
273
  /**
252
274
  * Base class for a single library section.
253
275
  */
254
- class LibrarySection extends plexObject_1.PlexObject {
276
+ export class LibrarySection extends PlexObject {
277
+ static { this.ALLOWED_FILTERS = []; }
278
+ static { this.ALLOWED_SORT = []; }
279
+ static { this.BOOLEAN_FILTERS = ['unwatched', 'duplicate']; }
255
280
  async all(sort = '') {
256
281
  let sortStr = '';
257
282
  if (sort) {
258
283
  sortStr = `?sort=${sort}`;
259
284
  }
260
285
  const key = `/library/sections/${this.key}/all${sortStr}`;
261
- const items = await baseFunctionality_1.fetchItems(this.server, key);
286
+ const items = await fetchItems(this.server, key);
262
287
  return items;
263
288
  }
264
289
  async agents() {
265
- return this.server.agents(search_1.searchType(this.type));
290
+ return this.server.agents(searchType(this.type));
266
291
  }
267
292
  /**
268
293
  * @param title Title of the item to return.
269
294
  * @returns the media item with the specified title.
270
295
  */
271
296
  async get(title) {
272
- const key = `/library/sections/${this.key}/all?title=${title}`;
273
- const data = await baseFunctionality_1.fetchItem(this.server, key, { title__iexact: title });
297
+ const key = `/library/sections/${this.key}/all?includeGuids=1&title=${title}`;
298
+ const data = await fetchItem(this.server, key, { title__iexact: title });
274
299
  return new this.VIDEO_TYPE(this.server, data, key, this);
275
300
  }
276
301
  /**
277
- * Searching within a library section is much more powerful. It seems certain
278
- * attributes on the media objects can be targeted to filter this search down
279
- * a bit, but I havent found the documentation for it.
302
+ * Returns the media item with the specified external IMDB, TMDB, or TVDB ID.
303
+ * Note: This search uses a PlexAPI operator so performance may be slow. All items from the
304
+ * entire Plex library need to be retrieved for each guid search. It is recommended to create
305
+ * your own lookup dictionary if you are searching for a lot of external guids.
306
+ *
307
+ * @param guid The external guid of the item to return.
308
+ * Examples: IMDB ``imdb://tt0944947``, TMDB ``tmdb://1399``, TVDB ``tvdb://121361``.
309
+ *
310
+ *
311
+ * Example:
312
+ *
313
+ * .. code-block:: python
314
+ *
315
+ * # This will retrieve all items in the entire library 3 times
316
+ * result1 = library.getGuid('imdb://tt0944947')
317
+ * result2 = library.getGuid('tmdb://1399')
318
+ * result3 = library.getGuid('tvdb://121361')
319
+ *
320
+ * # This will only retrieve all items in the library once to create a lookup dictionary
321
+ * guidLookup = {guid.id: item for item in library.all() for guid in item.guids}
322
+ * result1 = guidLookup['imdb://tt0944947']
323
+ * result2 = guidLookup['tmdb://1399']
324
+ * result3 = guidLookup['tvdb://121361']
325
+ */
326
+ async getGuid(guid) {
327
+ const key = `/library/sections/${this.key}/all?includeGuids=1`;
328
+ return fetchItem(this.server, key, { Guid__id__iexact: guid });
329
+ }
330
+ /**
331
+ * Returns the Plex Web URL for the library.
332
+ *
333
+ * @param base The base URL before the fragment (``#!``).
334
+ * Default is https://app.plex.tv/desktop.
335
+ * @param tab The library tab (recommended, library, collections, playlists, timeline).
336
+ * @param key A hub key.
337
+ */
338
+ getWebURL(base, tab, key) {
339
+ const params = new URLSearchParams();
340
+ params.append('source', this.key);
341
+ if (tab) {
342
+ params.append('pivot', tab);
343
+ }
344
+ if (key) {
345
+ params.append('key', key);
346
+ params.append('pageType', 'list');
347
+ }
348
+ return this.server._buildWebURL(base, undefined, params);
349
+ }
350
+ /**
351
+ * Search the library. The http requests will be batched in container_size. If you are only looking for the
352
+ * first <num> results, it would be wise to set the maxresults option to that amount so the search doesn't iterate
353
+ * over all results on the server.
280
354
  *
281
355
  * Example: "studio=Comedy%20Central" or "year=1999" "title=Kung Fu" all work. Other items
282
356
  * such as actor=<id> seem to work, but require you already know the id of the actor.
283
357
  * TLDR: This is untested but seems to work. Use library section search when you can.
284
358
  * @param args Search using a number of different attributes
285
359
  */
286
- async search(args = {}, libtype, Cls = this.VIDEO_TYPE) {
287
- const params = new url_1.URLSearchParams();
360
+ async search(args = {}, Cls = this.VIDEO_TYPE) {
361
+ const params = new URLSearchParams();
288
362
  for (const [key, value] of Object.entries(args)) {
289
363
  let strValue;
290
364
  if (typeof value === 'string') {
@@ -298,11 +372,11 @@ class LibrarySection extends plexObject_1.PlexObject {
298
372
  }
299
373
  params.append(key, strValue);
300
374
  }
301
- if (libtype) {
302
- params.append('type', search_1.searchType(libtype).toString());
375
+ if (args.libtype) {
376
+ params.append('type', searchType(args.libtype).toString());
303
377
  }
304
- const key = `/library/all/?${params.toString()}`;
305
- const data = await baseFunctionality_1.fetchItems(this.server, key, undefined, Cls, this);
378
+ const key = `/library/sections/${this.key}/all?${params.toString()}`;
379
+ const data = await fetchItems(this.server, key, undefined, Cls, this);
306
380
  return data;
307
381
  }
308
382
  /**
@@ -368,7 +442,7 @@ class LibrarySection extends plexObject_1.PlexObject {
368
442
  * @param kwargs object of settings to edit
369
443
  */
370
444
  async edit(kwargs) {
371
- const params = new url_1.URLSearchParams(kwargs);
445
+ const params = new URLSearchParams(kwargs);
372
446
  const part = `/library/sections/${this.key}?${params.toString()}`;
373
447
  await this.server.query(part, 'put');
374
448
  const library = await this.server.library();
@@ -382,7 +456,7 @@ class LibrarySection extends plexObject_1.PlexObject {
382
456
  }
383
457
  async hubs() {
384
458
  const key = `/hubs/sections/${this.key}`;
385
- const hubs = await baseFunctionality_1.fetchItems(this.server, key, undefined, Hub, this);
459
+ const hubs = await fetchItems(this.server, key, undefined, Hub, this);
386
460
  return hubs;
387
461
  }
388
462
  /**
@@ -390,10 +464,10 @@ class LibrarySection extends plexObject_1.PlexObject {
390
464
  */
391
465
  async playlists() {
392
466
  const key = `/playlists?type=15&playlistType=${this.CONTENT_TYPE}&sectionID=${this.key}`;
393
- return baseFunctionality_1.fetchItems(this.server, key, undefined, playlist_1.Playlist, this);
467
+ return fetchItems(this.server, key, undefined, Playlist, this);
394
468
  }
395
469
  async collections(args = {}) {
396
- const collections = await this.search(args, 'collection', Collections);
470
+ const collections = await this.search({ ...args, libtype: 'collection' }, Collections);
397
471
  collections.forEach(collection => {
398
472
  collection.VIDEO_TYPE = this.VIDEO_TYPE;
399
473
  });
@@ -404,7 +478,71 @@ class LibrarySection extends plexObject_1.PlexObject {
404
478
  */
405
479
  async folders() {
406
480
  const key = `/library/sections/${this.key}/folder`;
407
- return baseFunctionality_1.fetchItems(this.server, key, undefined, Folder);
481
+ return fetchItems(this.server, key, undefined, Folder);
482
+ }
483
+ async genres() {
484
+ const key = `/library/sections/${this.key}/genre`;
485
+ return fetchItems(this.server, key, undefined, FilterChoice);
486
+ }
487
+ /**
488
+ * Returns a list of available {@link FilteringFields} for a specified libtype.
489
+ * This is the list of options in the custom filter dropdown menu
490
+ */
491
+ async listFields(libtype = this.type) {
492
+ return (await this.getFilterType(libtype)).fields;
493
+ }
494
+ async getFilterType(libtype = this.type) {
495
+ const filterTypes = await this.filterTypes();
496
+ const filter = filterTypes.find(f => f.type === libtype);
497
+ if (!filter) {
498
+ throw new NotFound(`Unknown libtype "${libtype}" for this library.
499
+ Available libtypes: ${filterTypes.join(', ')}`);
500
+ }
501
+ return filter;
502
+ }
503
+ /**
504
+ * @param fieldType The data type for the field (tag, integer, string, boolean, date,
505
+ subtitleLanguage, audioLanguage, resolution).
506
+ */
507
+ async getFieldType(fieldType) {
508
+ const fieldTypes = await this.fieldTypes();
509
+ const fType = fieldTypes.find(f => f.type === fieldType);
510
+ if (!fType) {
511
+ const availableFieldTypes = fieldTypes.map(f => f.type);
512
+ throw new NotFound(`Unknown field type "${fieldType}" for this library. Available field types: ${availableFieldTypes.join(', ')}`);
513
+ }
514
+ return fType;
515
+ }
516
+ /**
517
+ * @param libtype The library type to filter (movie, show, season, episode,
518
+ * artist, album, track, photoalbum, photo, collection).
519
+ *
520
+ * @example
521
+ * ```ts
522
+ * const availableFilters = (await library.listFilters()).map(f => f.filter)
523
+ * ```
524
+ */
525
+ async listFilters(libtype) {
526
+ return (await this.getFilterType(libtype)).filters;
527
+ }
528
+ /**
529
+ * @param fieldType The data type for the field (tag, integer, string, boolean, date,
530
+ * subtitleLanguage, audioLanguage, resolution).
531
+ */
532
+ async listOperators(fieldType) {
533
+ return (await this.getFieldType(fieldType)).operators;
534
+ }
535
+ async filterTypes() {
536
+ if (this._filterTypes === null) {
537
+ await this._loadFilters();
538
+ }
539
+ return this._filterTypes;
540
+ }
541
+ async fieldTypes() {
542
+ if (this._fieldTypes === null) {
543
+ await this._loadFilters();
544
+ }
545
+ return this._fieldTypes;
408
546
  }
409
547
  _loadData(data) {
410
548
  this.uuid = data.uuid;
@@ -425,61 +563,99 @@ class LibrarySection extends plexObject_1.PlexObject {
425
563
  this.createdAt = new Date(data.createdAt * 1000);
426
564
  this.scannedAt = new Date(data.scannedAt * 1000);
427
565
  }
566
+ async _loadFilters() {
567
+ const key = `/library/sections/${this.key}/all?includeMeta=1&includeAdvanced=1&X-Plex-Container-Start=0&X-Plex-Container-Size=0`;
568
+ const data = await this.server.query(key);
569
+ // rtag='Meta'
570
+ this._filterTypes = findItems(data, undefined, FilteringType);
571
+ }
428
572
  }
429
- exports.LibrarySection = LibrarySection;
430
- LibrarySection.ALLOWED_FILTERS = [];
431
- LibrarySection.ALLOWED_SORT = [];
432
- LibrarySection.BOOLEAN_FILTERS = ['unwatched', 'duplicate'];
433
- class MovieSection extends LibrarySection {
573
+ export class MovieSection extends LibrarySection {
434
574
  constructor() {
435
575
  super(...arguments);
436
576
  this.METADATA_TYPE = 'movie';
437
577
  this.CONTENT_TYPE = 'video';
438
- this.VIDEO_TYPE = video_1.Movie;
439
- }
578
+ this.VIDEO_TYPE = Movie;
579
+ }
580
+ static { this.TYPE = 'movie'; }
581
+ static { this.ALLOWED_FILTERS = [
582
+ 'unwatched',
583
+ 'duplicate',
584
+ 'year',
585
+ 'decade',
586
+ 'genre',
587
+ 'contentRating',
588
+ 'collection',
589
+ 'director',
590
+ 'actor',
591
+ 'country',
592
+ 'studio',
593
+ 'resolution',
594
+ 'guid',
595
+ 'label',
596
+ 'writer',
597
+ 'producer',
598
+ 'subtitleLanguage',
599
+ 'audioLanguage',
600
+ 'lastViewedAt',
601
+ 'viewCount',
602
+ 'addedAt',
603
+ ]; }
604
+ static { this.ALLOWED_SORT = [
605
+ 'addedAt',
606
+ 'originallyAvailableAt',
607
+ 'lastViewedAt',
608
+ 'titleSort',
609
+ 'rating',
610
+ 'mediaHeight',
611
+ 'duration',
612
+ ]; }
613
+ static { this.TAG = 'Directory'; }
440
614
  }
441
- exports.MovieSection = MovieSection;
442
- MovieSection.TYPE = 'movie';
443
- MovieSection.ALLOWED_FILTERS = [
444
- 'unwatched',
445
- 'duplicate',
446
- 'year',
447
- 'decade',
448
- 'genre',
449
- 'contentRating',
450
- 'collection',
451
- 'director',
452
- 'actor',
453
- 'country',
454
- 'studio',
455
- 'resolution',
456
- 'guid',
457
- 'label',
458
- 'writer',
459
- 'producer',
460
- 'subtitleLanguage',
461
- 'audioLanguage',
462
- 'lastViewedAt',
463
- 'viewCount',
464
- 'addedAt',
465
- ];
466
- MovieSection.ALLOWED_SORT = [
467
- 'addedAt',
468
- 'originallyAvailableAt',
469
- 'lastViewedAt',
470
- 'titleSort',
471
- 'rating',
472
- 'mediaHeight',
473
- 'duration',
474
- ];
475
- MovieSection.TAG = 'Directory';
476
- class ShowSection extends LibrarySection {
615
+ export class ShowSection extends LibrarySection {
477
616
  constructor() {
478
617
  super(...arguments);
479
618
  this.METADATA_TYPE = 'episode';
480
619
  this.CONTENT_TYPE = 'video';
481
- this.VIDEO_TYPE = video_1.Show;
482
- }
620
+ this.VIDEO_TYPE = Show;
621
+ }
622
+ static { this.TYPE = 'show'; }
623
+ static { this.ALLOWED_FILTERS = [
624
+ 'unwatched',
625
+ 'year',
626
+ 'genre',
627
+ 'contentRating',
628
+ 'network',
629
+ 'collection',
630
+ 'guid',
631
+ 'duplicate',
632
+ 'label',
633
+ 'show.title',
634
+ 'show.year',
635
+ 'show.userRating',
636
+ 'show.viewCount',
637
+ 'show.lastViewedAt',
638
+ 'show.actor',
639
+ 'show.addedAt',
640
+ 'episode.title',
641
+ 'episode.originallyAvailableAt',
642
+ 'episode.resolution',
643
+ 'episode.subtitleLanguage',
644
+ 'episode.unwatched',
645
+ 'episode.addedAt',
646
+ 'episode.userRating',
647
+ 'episode.viewCount',
648
+ 'episode.lastViewedAt',
649
+ ]; }
650
+ static { this.ALLOWED_SORT = [
651
+ 'addedAt',
652
+ 'lastViewedAt',
653
+ 'originallyAvailableAt',
654
+ 'titleSort',
655
+ 'rating',
656
+ 'unwatched',
657
+ ]; }
658
+ static { this.TAG = 'Directory'; }
483
659
  // TODO: figure out how to return episode objects
484
660
  // /**
485
661
  // * Search for an episode. See :func:`~plexapi.library.LibrarySection.search` for usage.
@@ -496,46 +672,9 @@ class ShowSection extends LibrarySection {
496
672
  return this.search({ libtype, maxresults, sort: 'episode.addedAt:desc', ...args });
497
673
  }
498
674
  }
499
- exports.ShowSection = ShowSection;
500
- ShowSection.TYPE = 'show';
501
- ShowSection.ALLOWED_FILTERS = [
502
- 'unwatched',
503
- 'year',
504
- 'genre',
505
- 'contentRating',
506
- 'network',
507
- 'collection',
508
- 'guid',
509
- 'duplicate',
510
- 'label',
511
- 'show.title',
512
- 'show.year',
513
- 'show.userRating',
514
- 'show.viewCount',
515
- 'show.lastViewedAt',
516
- 'show.actor',
517
- 'show.addedAt',
518
- 'episode.title',
519
- 'episode.originallyAvailableAt',
520
- 'episode.resolution',
521
- 'episode.subtitleLanguage',
522
- 'episode.unwatched',
523
- 'episode.addedAt',
524
- 'episode.userRating',
525
- 'episode.viewCount',
526
- 'episode.lastViewedAt',
527
- ];
528
- ShowSection.ALLOWED_SORT = [
529
- 'addedAt',
530
- 'lastViewedAt',
531
- 'originallyAvailableAt',
532
- 'titleSort',
533
- 'rating',
534
- 'unwatched',
535
- ];
536
- ShowSection.TAG = 'Directory';
537
675
  /** Represents a single Hub (or category) in the PlexServer search */
538
- class Hub extends plexObject_1.PlexObject {
676
+ export class Hub extends PlexObject {
677
+ static { this.TAG = 'Hub'; }
539
678
  _loadData(data) {
540
679
  this.hubIdentifier = data.hubIdentifier;
541
680
  this.size = data.size;
@@ -545,33 +684,31 @@ class Hub extends plexObject_1.PlexObject {
545
684
  this.Metadata = data.Metadata;
546
685
  }
547
686
  }
548
- exports.Hub = Hub;
549
- Hub.TAG = 'Hub';
550
687
  /**
551
688
  * Represents a Folder inside a library.
552
689
  */
553
- class Folder extends plexObject_1.PlexObject {
690
+ export class Folder extends PlexObject {
554
691
  /**
555
692
  * Returns a list of available Folders for this folder.
556
693
  * Continue down subfolders until a mediaType is found.
557
694
  */
558
695
  async subfolders() {
559
696
  if (this.key.startsWith('/library/metadata')) {
560
- return baseFunctionality_1.fetchItems(this.server, this.key);
697
+ return fetchItems(this.server, this.key);
561
698
  }
562
- return baseFunctionality_1.fetchItems(this.server, this.key, undefined, Folder);
699
+ return fetchItems(this.server, this.key, undefined, Folder);
563
700
  }
564
701
  _loadData(data) {
565
702
  this.key = data.key;
566
703
  this.title = data.title;
567
704
  }
568
705
  }
569
- exports.Folder = Folder;
570
- class Collections extends partialPlexObject_1.PartialPlexObject {
706
+ export class Collections extends PartialPlexObject {
571
707
  constructor() {
572
708
  super(...arguments);
573
709
  this.TYPE = 'collection';
574
710
  }
711
+ static { this.TAG = 'Directory'; }
575
712
  // Alias for childCount
576
713
  get size() {
577
714
  return this.childCount;
@@ -582,10 +719,12 @@ class Collections extends partialPlexObject_1.PartialPlexObject {
582
719
  async items() {
583
720
  const key = `/library/metadata/${this.ratingKey}/children`;
584
721
  const data = await this.server.query(key);
585
- return data.MediaContainer.Metadata.map(data => new this.VIDEO_TYPE(this.server, data, undefined, this));
722
+ return (data.MediaContainer.Metadata?.map(d => new this.VIDEO_TYPE(this.server, d, undefined, this)) ?? []);
723
+ }
724
+ _loadFullData(data) {
725
+ this._loadData(data);
586
726
  }
587
727
  _loadData(data) {
588
- var _a, _b;
589
728
  this.key = data.key;
590
729
  this.title = data.title;
591
730
  this.ratingKey = data.ratingKey;
@@ -601,14 +740,91 @@ class Collections extends partialPlexObject_1.PartialPlexObject {
601
740
  this.thumb = data.thumb;
602
741
  this.addedAt = data.addedAt;
603
742
  this.updatedAt = data.updatedAt;
604
- this.childCount = parseInt((_b = (_a = data.childCount) !== null && _a !== void 0 ? _a : data.size) !== null && _b !== void 0 ? _b : 0, 10);
743
+ this.childCount = Number(data.childCount ?? data.size ?? '0');
605
744
  this.maxYear = data.maxYear;
606
745
  this.minYear = data.minYear;
607
746
  this.art = data.art;
608
747
  }
609
- _loadFullData(data) {
610
- this._loadData(data);
748
+ }
749
+ export class FilterChoice extends PlexObject {
750
+ static { this.TAG = 'Directory'; }
751
+ _loadData(data) {
752
+ this.key = data.key;
753
+ this.title = data.title;
754
+ this.type = data.type;
755
+ this.thumb = data.thumb;
756
+ }
757
+ }
758
+ export class FilteringType extends PlexObject {
759
+ static { this.TAG = 'Type'; }
760
+ _loadData(data) {
761
+ this.active = data.active;
762
+ this.fields = findItems(data, undefined, FilteringField);
763
+ this.filters = findItems(data, undefined, FilteringFilter);
764
+ this.key = data.key;
765
+ this.sorts = findItems(data, undefined, FilteringSort);
766
+ this.title = data.title;
767
+ this.type = data.type;
768
+ }
769
+ }
770
+ /**
771
+ * Represents a single Filter object for a {@link FilteringType}
772
+ */
773
+ export class FilteringFilter extends PlexObject {
774
+ static { this.TAG = 'Filter'; }
775
+ _loadData(data) {
776
+ this.filter = data.filter;
777
+ this.filterType = data.filterType;
778
+ this.key = data.key;
779
+ this.title = data.title;
780
+ this.type = data.type;
781
+ }
782
+ }
783
+ /**
784
+ * Represents a single Sort object for a {@link FilteringType}
785
+ */
786
+ export class FilteringSort extends PlexObject {
787
+ static { this.TAG = 'Sort'; }
788
+ _loadData(data) {
789
+ this.active = data.active;
790
+ this.activeDirection = data.activeDirection;
791
+ this.default = data.default;
792
+ this.defaultDirection = data.defaultDirection;
793
+ this.descKey = data.descKey;
794
+ this.firstCharacterKey = data.firstCharacterKey;
795
+ this.key = data.key;
796
+ this.title = data.title;
797
+ }
798
+ }
799
+ /**
800
+ * Represents a single Field object for a {@link FilteringType}
801
+ */
802
+ export class FilteringField extends PlexObject {
803
+ static { this.TAG = 'Field'; }
804
+ _loadData(data) {
805
+ this.key = data.key;
806
+ this.title = data.title;
807
+ this.type = data.type;
808
+ this.subType = data.subType;
809
+ }
810
+ }
811
+ /**
812
+ * Represents a single FieldType for library filtering.
813
+ */
814
+ export class FilteringFieldType extends PlexObject {
815
+ static { this.TAG = 'FieldType'; }
816
+ _loadData(data) {
817
+ this.type = data.type;
818
+ this.operators = findItems(data, undefined, FilteringOperator);
819
+ }
820
+ }
821
+ /**
822
+ * Represents a single FilterChoice for library filtering.
823
+ */
824
+ export class FilteringOperator extends PlexObject {
825
+ static { this.TAG = 'Operator'; }
826
+ _loadData(data) {
827
+ this.key = data.key;
828
+ this.type = data.type;
611
829
  }
612
830
  }
613
- exports.Collections = Collections;
614
- Collections.TAG = 'Directory';