diff --git a/.github/workflows/build-and-copy-pms-spec.yml b/.github/workflows/build-and-copy-pms-spec.yml index 8cd075b2..d4fe5f58 100644 --- a/.github/workflows/build-and-copy-pms-spec.yml +++ b/.github/workflows/build-and-copy-pms-spec.yml @@ -27,8 +27,8 @@ jobs: commit_message: "build: dereferenced Plex Media Server API Spec updated" skip_checkout: true skip_fetch: true - add_options: '-f' - file_pattern: './output/*.yaml' + add_options: "-f" + file_pattern: "./output/*.yaml" skip_dirty_check: true - name: Pushes Dereferenced Specification File @@ -42,4 +42,3 @@ jobs: user_email: lukeslakemail@gmail.com user_name: lukehagar commit_message: Updating PMS Spec - diff --git a/.github/workflows/steps/project-setup/action.yaml b/.github/workflows/steps/project-setup/action.yaml index bbf12fa7..a70788ba 100644 --- a/.github/workflows/steps/project-setup/action.yaml +++ b/.github/workflows/steps/project-setup/action.yaml @@ -1,12 +1,12 @@ -name: 'Project Setup' -description: 'Setup Bun and install dependencies' +name: "Project Setup" +description: "Setup Bun and install dependencies" runs: - using: 'composite' + using: "composite" steps: - - name: Setup Bun - uses: oven-sh/setup-bun@v2 - with: - bun-version: "latest" - - name: Clean install node_modules - shell: bash - run: bun install + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: "latest" + - name: Clean install node_modules + shell: bash + run: bun install diff --git a/.speakeasy/workflow.yaml b/.speakeasy/workflow.yaml index 68bb0058..a9cab3a0 100644 --- a/.speakeasy/workflow.yaml +++ b/.speakeasy/workflow.yaml @@ -1,9 +1,9 @@ workflowVersion: 1.0.0 speakeasyVersion: latest sources: - Plex-API: - inputs: - - location: ./src/pms-spec.yaml - registry: - location: registry.speakeasyapi.dev/lukehagar/lukehagar/plex-api + Plex-API: + inputs: + - location: ./src/pms-spec.yaml + registry: + location: registry.speakeasyapi.dev/lukehagar/lukehagar/plex-api targets: {} diff --git a/README.md b/README.md index 743a62c0..32fa85ff 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,28 @@ # Plex Media Server OpenAPI Specification Automation and SDKs provided by [Speakeasy](https://speakeasyapi.dev/) - + An Open Source OpenAPI Specification for Plex Media Server ## Documentation -[API Documentation](https://plexapi.dev) + +[API Documentation](https://plexapi.dev) ## SDKs + The following SDKs are generated from the OpenAPI Specification. They are automatically generated and may not be fully tested. If you find any issues, please open an issue on the respective repository. -| Language | Repository | Releases | Other | -| -------- | ---------- | ------- | ----- | -| Python | [GitHub](https://github.com/LukeHagar/plexpy) | [PyPI](https://pypi.org/project/plex-api-client/) | - | -| JavaScript/TypeScript | [GitHub](https://github.com/LukeHagar/plexjs) | [NPM](https://www.npmjs.com/package/@lukehagar/plexjs) \ [JSR](https://jsr.io/@lukehagar/plexjs) | - | -| Go | [GitHub](https://github.com/LukeHagar/plexgo) | [Releases](https://github.com/LukeHagar/plexgo/releases) | [GoDoc](https://pkg.go.dev/github.com/LukeHagar/plexgo) | -| Ruby | [GitHub](https://github.com/LukeHagar/plexruby) | [Releases](https://github.com/LukeHagar/plexruby/releases) | - | -| Swift | [GitHub](https://github.com/LukeHagar/plexswift) | [Releases](https://github.com/LukeHagar/plexswift/releases) | - | -| PHP | [GitHub](https://github.com/LukeHagar/plexphp) | [Releases](https://github.com/LukeHagar/plexphp/releases) | - | -| Java | [GitHub](https://github.com/LukeHagar/plexjava) | [Releases](https://github.com/LukeHagar/plexjava/releases) | - | -| C# | [GitHub](https://github.com/LukeHagar/plexcsharp) | [Releases](https://github.com/LukeHagar/plexcsharp/releases) | - | +| Language | Repository | Releases | Other | +| --------------------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------- | +| Python | [GitHub](https://github.com/LukeHagar/plexpy) | [PyPI](https://pypi.org/project/plex-api-client/) | - | +| JavaScript/TypeScript | [GitHub](https://github.com/LukeHagar/plexjs) | [NPM](https://www.npmjs.com/package/@lukehagar/plexjs) \ [JSR](https://jsr.io/@lukehagar/plexjs) | - | +| Go | [GitHub](https://github.com/LukeHagar/plexgo) | [Releases](https://github.com/LukeHagar/plexgo/releases) | [GoDoc](https://pkg.go.dev/github.com/LukeHagar/plexgo) | +| Ruby | [GitHub](https://github.com/LukeHagar/plexruby) | [Releases](https://github.com/LukeHagar/plexruby/releases) | - | +| Swift | [GitHub](https://github.com/LukeHagar/plexswift) | [Releases](https://github.com/LukeHagar/plexswift/releases) | - | +| PHP | [GitHub](https://github.com/LukeHagar/plexphp) | [Releases](https://github.com/LukeHagar/plexphp/releases) | - | +| Java | [GitHub](https://github.com/LukeHagar/plexjava) | [Releases](https://github.com/LukeHagar/plexjava/releases) | - | +| C# | [GitHub](https://github.com/LukeHagar/plexcsharp) | [Releases](https://github.com/LukeHagar/plexcsharp/releases) | - | + +## Questions? -## Questions? Reach out to me on the [Discord Server](https://discord.gg/mxqjsJHwUm) diff --git a/src/models/AuthPinContainer.yaml b/src/models/AuthPinContainer.yaml index d7cd9e29..dfa6d80f 100644 --- a/src/models/AuthPinContainer.yaml +++ b/src/models/AuthPinContainer.yaml @@ -3,9 +3,9 @@ x-examples: Example 1: id: 373040866 code: 7RQZ - product: '0' + product: "0" trusted: false - qr: 'https://plex.tv/api/v2/pins/qr/7RQZ' + qr: "https://plex.tv/api/v2/pins/qr/7RQZ" clientIdentifier: 9klpwueublnfbvlx95w83ah9 location: code: NL @@ -14,23 +14,23 @@ x-examples: country: The Netherlands city: Breda time_zone: Europe/Amsterdam - postal_code: '4814' + postal_code: "4814" in_privacy_restricted_country: true in_privacy_restricted_region: true subdivisions: North Brabant - coordinates: '51.5869, 4.7471' + coordinates: "51.5869, 4.7471" expiresIn: 876 - createdAt: '2024-07-16T17:03:05Z' - expiresAt: '2024-07-16T17:18:05Z' + createdAt: "2024-07-16T17:03:05Z" + expiresAt: "2024-07-16T17:18:05Z" authToken: null newRegistration: null title: AuthPinContainer examples: - id: 308667304 code: 7RQZ - product: '0' + product: "0" trusted: false - qr: 'https://plex.tv/api/v2/pins/qr/7RQZ' + qr: "https://plex.tv/api/v2/pins/qr/7RQZ" clientIdentifier: string location: code: VI @@ -43,10 +43,10 @@ examples: in_privacy_restricted_country: true in_privacy_restricted_region: true subdivisions: Saint Thomas - coordinates: '18.3381, -64.8941' + coordinates: "18.3381, -64.8941" expiresIn: 876 - createdAt: '2024-07-16T17:03:05Z' - expiresAt: '2024-07-16T17:18:05Z' + createdAt: "2024-07-16T17:03:05Z" + expiresAt: "2024-07-16T17:18:05Z" authToken: null newRegistration: null required: @@ -69,13 +69,13 @@ properties: example: 7RQZ product: type: string - example: '0' + example: "0" trusted: type: boolean default: false qr: type: string - example: 'https://plex.tv/api/v2/pins/qr/7RQZ' + example: "https://plex.tv/api/v2/pins/qr/7RQZ" clientIdentifier: type: string description: The X-Client-Identifier used in the request @@ -84,17 +84,17 @@ properties: expiresIn: type: integer example: 876 - description: 'The number of seconds this pin expires, by default 900 seconds' + description: "The number of seconds this pin expires, by default 900 seconds" default: 900 createdAt: type: string - example: '2024-07-16T17:03:05Z' + example: "2024-07-16T17:03:05Z" format: date-time expiresAt: type: string format: date-time - example: '2024-07-16T17:18:05Z' + example: "2024-07-16T17:18:05Z" authToken: - type: 'null' + type: "null" newRegistration: - type: 'null' + type: "null" diff --git a/src/models/Friend.yaml b/src/models/Friend.yaml index 445acf9a..63564742 100644 --- a/src/models/Friend.yaml +++ b/src/models/Friend.yaml @@ -9,7 +9,7 @@ examples: sharedServers: [] sharedSources: [] status: accepted - thumb: 'https://plex.tv/users/7d1916e0d8f6e76b/avatar?c=1694481578' + thumb: "https://plex.tv/users/7d1916e0d8f6e76b/avatar?c=1694481578" title: username123 username: username123 uuid: 7d1916e0d8f6e76b @@ -38,7 +38,7 @@ properties: - null type: - string - - 'null' + - "null" description: The account full name home: type: boolean @@ -67,7 +67,7 @@ properties: description: Current friend request status thumb: type: string - example: 'https://plex.tv/users/7d1916e0d8f6e76b/avatar?c=1694481578' + example: "https://plex.tv/users/7d1916e0d8f6e76b/avatar?c=1694481578" format: uri description: URL of the account thumbnail title: diff --git a/src/models/GeoData.yaml b/src/models/GeoData.yaml index 81598c9c..c89e4580 100644 --- a/src/models/GeoData.yaml +++ b/src/models/GeoData.yaml @@ -1,6 +1,6 @@ title: GeoData type: object -description: 'Geo location data' +description: "Geo location data" examples: - code: VI continent_code: NA @@ -12,7 +12,7 @@ examples: in_privacy_restricted_country: true in_privacy_restricted_region: true subdivisions: Saint Thomas - coordinates: '18.3381, -64.8941' + coordinates: "18.3381, -64.8941" required: - code - continent_code @@ -71,5 +71,5 @@ properties: example: Saint Thomas coordinates: type: string - description: 'The geographical coordinates (latitude, longitude) of the location.' - example: '18.3381, -64.8941' + description: "The geographical coordinates (latitude, longitude) of the location." + example: "18.3381, -64.8941" diff --git a/src/models/PastSubscription.yaml b/src/models/PastSubscription.yaml index 19759a29..13dd3b82 100644 --- a/src/models/PastSubscription.yaml +++ b/src/models/PastSubscription.yaml @@ -20,19 +20,19 @@ properties: id: type: - string - - 'null' + - "null" mode: type: - string - - 'null' + - "null" renewsAt: oneOf: - - $ref: './common/PlexDateTime.yaml' - - type: 'null' + - $ref: "./common/PlexDateTime.yaml" + - type: "null" endsAt: oneOf: - - $ref: './common/PlexDateTime.yaml' - - type: 'null' + - $ref: "./common/PlexDateTime.yaml" + - type: "null" canceled: type: boolean example: false @@ -67,7 +67,7 @@ properties: transfer: type: - string - - 'null' + - "null" state: example: ended enum: @@ -83,4 +83,4 @@ properties: paymentMethodId: type: - integer - - 'null' + - "null" diff --git a/src/models/Subscription.yaml b/src/models/Subscription.yaml index f59934a9..6be69d11 100644 --- a/src/models/Subscription.yaml +++ b/src/models/Subscription.yaml @@ -73,8 +73,8 @@ properties: description: Date the account subscribed to Plex Pass type: - string - - 'null' - example: '2021-04-12T18:21:12Z' + - "null" + example: "2021-04-12T18:21:12Z" status: description: String representation of subscriptionActive example: Inactive @@ -85,9 +85,9 @@ properties: description: Payment service used for your Plex Pass subscription type: - string - - 'null' + - "null" plan: description: Name of Plex Pass subscription plan type: - string - - 'null' + - "null" diff --git a/src/models/UserPlexAccount.yaml b/src/models/UserPlexAccount.yaml index df7bd951..59e280f6 100644 --- a/src/models/UserPlexAccount.yaml +++ b/src/models/UserPlexAccount.yaml @@ -2,8 +2,8 @@ title: UserPlexAccount type: object examples: - adsConsent: true - adsConsentReminderAt: '2019-08-24T14:15:22Z' - adsConsentSetAt: '2019-08-24T14:15:22Z' + adsConsentReminderAt: "2019-08-24T14:15:22Z" + adsConsentSetAt: "2019-08-24T14:15:22Z" anonymous: false authToken: CxoUzBTSV5hsxjTpFKaf backupCodesCreated: false @@ -29,14 +29,14 @@ examples: - id: string mode: string renewsAt: string - endsAt: '1556281940' - canceled: '0' - gracePeriod: '0' - onHold: '0' - canReactivate: '0' - canUpgrade: '0' - canDowngrade: '0' - canConvert: '0' + endsAt: "1556281940" + canceled: "0" + gracePeriod: "0" + onHold: "0" + canReactivate: "0" + canUpgrade: "0" + canDowngrade: "0" + canConvert: "0" type: plexpass transfer: string state: ended @@ -58,10 +58,10 @@ examples: restricted: false roles: - string - scrobbleTypes: '' + scrobbleTypes: "" services: - identifier: metadata-dev - endpoint: 'https://epg.provider.plex.tv' + endpoint: "https://epg.provider.plex.tv" token: DjoMtqFAGRL1uVtCyF1dKIorTbShJeqv secret: string status: online @@ -69,7 +69,7 @@ examples: features: - Android - Dolby Vision active: true - subscribedAt: '2021-04-12T18:21:12Z' + subscribedAt: "2021-04-12T18:21:12Z" status: Inactive paymentService: string plan: string @@ -78,11 +78,11 @@ examples: - features: - Android - Dolby Vision active: true - subscribedAt: '2021-04-12T18:21:12Z' + subscribedAt: "2021-04-12T18:21:12Z" status: Inactive paymentService: string plan: string - thumb: 'https://plex.tv/users/a4f43c1ebfde43a5/avatar?c=8372075101' + thumb: "https://plex.tv/users/a4f43c1ebfde43a5/avatar?c=8372075101" title: UsernameTitle trials: - {} @@ -132,24 +132,24 @@ properties: adsConsent: type: - boolean - - 'null' + - "null" description: Unknown adsConsentReminderAt: type: - string - - 'null' + - "null" description: Unknown format: date-time adsConsentSetAt: type: - string - - 'null' + - "null" description: Unknown format: date-time anonymous: type: - boolean - - 'null' + - "null" description: Unknown default: false authToken: @@ -192,7 +192,7 @@ properties: description: List of devices your allowed to use with this account items: type: string - example: '[]' + example: "[]" guest: type: boolean description: If the account is a Plex Home guest user @@ -227,7 +227,7 @@ properties: locale: type: - string - - 'null' + - "null" description: The account locale mailingListActive: type: boolean @@ -245,7 +245,7 @@ properties: format: int32 pin: type: string - description: '[Might be removed] The hashed Plex Home PIN ' + description: "[Might be removed] The hashed Plex Home PIN " deprecated: true profile: $ref: ./UserProfile.yaml @@ -264,7 +264,7 @@ properties: default: false roles: type: array - description: '[Might be removed] List of account roles. Plexpass membership listed here' + description: "[Might be removed] List of account roles. Plexpass membership listed here" items: type: string scrobbleTypes: @@ -286,17 +286,17 @@ properties: example: metadata-dev endpoint: type: string - example: 'https://epg.provider.plex.tv' + example: "https://epg.provider.plex.tv" format: uri token: type: - string - - 'null' + - "null" example: DjoMtqFAGRL1uVtCyF1dKIorTbShJeqv secret: type: - string - - 'null' + - "null" status: example: online enum: @@ -308,7 +308,7 @@ properties: subscriptionDescription: type: - string - - 'null' + - "null" description: Description of the Plex Pass subscription subscriptions: type: array @@ -318,7 +318,7 @@ properties: type: string description: URL of the account thumbnail format: uri - example: 'https://plex.tv/users/a4f43c1ebfde43a5/avatar?c=8372075101' + example: "https://plex.tv/users/a4f43c1ebfde43a5/avatar?c=8372075101" title: type: string description: The title of the account (username or friendly name) diff --git a/src/models/UserProfile.yaml b/src/models/UserProfile.yaml index b358d294..7be3b8c4 100644 --- a/src/models/UserProfile.yaml +++ b/src/models/UserProfile.yaml @@ -12,18 +12,18 @@ properties: defaultAudioLanguage: type: - string - - 'null' + - "null" example: ja description: The preferred audio language for the account defaultSubtitleLanguage: type: - string - - 'null' + - "null" example: en description: The preferred subtitle language for the account autoSelectSubtitle: example: 1 - description: 'The auto-select subtitle mode (0 = Manually selected, 1 = Shown with foreign audio, 2 = Always enabled)' + description: "The auto-select subtitle mode (0 = Manually selected, 1 = Shown with foreign audio, 2 = Always enabled)" enum: - 0 - 1 @@ -32,13 +32,13 @@ properties: - 0 - 1 example: 1 - description: 'The subtitles for the deaf or hard-of-hearing (SDH) searches mode (0 = Prefer non-SDH subtitles, 1 = Prefer SDH subtitles, 2 = Only show SDH subtitles, 3 = Only shown non-SDH subtitles)' + description: "The subtitles for the deaf or hard-of-hearing (SDH) searches mode (0 = Prefer non-SDH subtitles, 1 = Prefer SDH subtitles, 2 = Only show SDH subtitles, 3 = Only shown non-SDH subtitles)" defaultSubtitleForced: enum: - 0 - 1 example: 0 - description: 'The forced subtitles searches mode (0 = Prefer non-forced subtitles, 1 = Prefer forced subtitles, 2 = Only show forced subtitles, 3 = Only show non-forced subtitles)' + description: "The forced subtitles searches mode (0 = Prefer non-forced subtitles, 1 = Prefer forced subtitles, 2 = Only show forced subtitles, 3 = Only show non-forced subtitles)" watchedIndicator: enum: - 0 diff --git a/src/models/common/PlexDateTime.yaml b/src/models/common/PlexDateTime.yaml index 211d87ff..be4cfefc 100644 --- a/src/models/common/PlexDateTime.yaml +++ b/src/models/common/PlexDateTime.yaml @@ -2,4 +2,4 @@ type: - integer example: 1556281940 description: Unix epoch datetime -format: int32 \ No newline at end of file +format: int32 diff --git a/src/parameters/library/rating-key.yaml b/src/parameters/library/rating-key.yaml index 233a2c45..5253c2f6 100644 --- a/src/parameters/library/rating-key.yaml +++ b/src/parameters/library/rating-key.yaml @@ -5,4 +5,3 @@ schema: type: integer example: 9518 required: true - diff --git a/src/parameters/library/section-key.yaml b/src/parameters/library/section-key.yaml index d731089c..0f99d753 100644 --- a/src/parameters/library/section-key.yaml +++ b/src/parameters/library/section-key.yaml @@ -1,5 +1,5 @@ name: sectionKey -description: | +description: | The unique key of the Plex library. Note: This is unique in the context of the Plex server. in: path @@ -8,4 +8,3 @@ schema: format: int32 example: 9518 required: true - diff --git a/src/parameters/plex/x-plex-device.yaml b/src/parameters/plex/x-plex-device.yaml index debee594..4f8d5034 100644 --- a/src/parameters/plex/x-plex-device.yaml +++ b/src/parameters/plex/x-plex-device.yaml @@ -4,4 +4,3 @@ required: false schema: type: string example: "Linux" - diff --git a/src/paths/colon/timeline.yaml b/src/paths/colon/timeline.yaml index 7c7a48d5..6c09954f 100644 --- a/src/paths/colon/timeline.yaml +++ b/src/paths/colon/timeline.yaml @@ -4,12 +4,12 @@ get: summary: Get the timeline for a media item description: Get the timeline for a media item operationId: getTimeline - parameters: + parameters: - name: ratingKey description: The rating key of the media item required: true in: query - schema: + schema: type: number example: 23409 @@ -17,7 +17,7 @@ get: description: The key of the media item to get the timeline for required: true in: query - schema: + schema: type: string example: "/library/metadata/23409" @@ -25,7 +25,7 @@ get: description: The state of the media item required: true in: query - schema: + schema: type: string enum: ["playing", "paused", "stopped"] example: "playing" @@ -34,7 +34,7 @@ get: description: Whether the media item has MDE required: true in: query - schema: + schema: type: number example: 1 @@ -42,7 +42,7 @@ get: description: The time of the media item required: true in: query - schema: + schema: type: number example: 2000 @@ -50,7 +50,7 @@ get: description: The duration of the media item required: true in: query - schema: + schema: type: number example: 10000 @@ -58,7 +58,7 @@ get: description: The context of the media item required: true in: query - schema: + schema: type: string example: "home:hub.continueWatching" @@ -66,7 +66,7 @@ get: description: The play queue item ID of the media item required: true in: query - schema: + schema: type: number example: 1 @@ -74,7 +74,7 @@ get: description: The playback time of the media item required: true in: query - schema: + schema: type: number example: 2000 @@ -82,7 +82,7 @@ get: description: The row of the media item required: true in: query - schema: + schema: type: number example: 1 diff --git a/src/paths/companions/companions.yaml b/src/paths/companions/companions.yaml index 757d1562..013de288 100644 --- a/src/paths/companions/companions.yaml +++ b/src/paths/companions/companions.yaml @@ -1,13 +1,13 @@ get: servers: - - url: 'https://plex.tv/api/v2' + - url: "https://plex.tv/api/v2" tags: - Plex summary: Get Companions Data description: Get Companions Data operationId: getCompanionsData responses: - '200': + "200": description: Companions Data content: application/json: @@ -28,21 +28,21 @@ get: example: tv.plex.sonos baseURL: type: string - example: 'https://sonos.plex.tv' + example: "https://sonos.plex.tv" format: uri title: type: string example: Sonos linkURL: type: string - example: 'https://sonos.plex.tv/link' + example: "https://sonos.plex.tv/link" provides: type: string - example: 'client,player' + example: "client,player" token: type: string description: The plex authtoken used to identify with - '400': + "400": $ref: ../../responses/400.yaml - '401': + "401": $ref: ../../responses/401.yaml diff --git a/src/paths/friends/friends.yaml b/src/paths/friends/friends.yaml index 80066ef1..1cb12184 100644 --- a/src/paths/friends/friends.yaml +++ b/src/paths/friends/friends.yaml @@ -1,13 +1,13 @@ get: servers: - - url: 'https://plex.tv/api/v2' + - url: "https://plex.tv/api/v2" tags: - Plex summary: Get list of friends of the user logged in description: Get friends of provided auth token. operationId: getUserFriends responses: - '200': + "200": description: Friends Data content: application/json: @@ -15,7 +15,7 @@ get: type: array items: $ref: ../../models/Friend.yaml - '400': - $ref: '../../responses/400.yaml' - '401': - $ref: '../../responses/401.yaml' + "400": + $ref: "../../responses/400.yaml" + "401": + $ref: "../../responses/401.yaml" diff --git a/src/paths/geoip/geoip.yaml b/src/paths/geoip/geoip.yaml index 859470b1..3eaadfa7 100644 --- a/src/paths/geoip/geoip.yaml +++ b/src/paths/geoip/geoip.yaml @@ -1,6 +1,6 @@ get: servers: - - url: 'https://plex.tv/api/v2' + - url: "https://plex.tv/api/v2" security: [] # No security required tags: - Plex @@ -8,13 +8,13 @@ get: description: Returns the geolocation and locale data of the caller operationId: getGeoData responses: - '200': + "200": description: Gets the geo location data of the user content: application/json: schema: $ref: ../../models/GeoData.yaml - '400': + "400": $ref: ../../responses/400.yaml - '401': + "401": $ref: ../../responses/401.yaml diff --git a/src/paths/library/[sectionKey]/get-search-library.yaml b/src/paths/library/[sectionKey]/get-search-library.yaml index 2b274f54..7c440b54 100644 --- a/src/paths/library/[sectionKey]/get-search-library.yaml +++ b/src/paths/library/[sectionKey]/get-search-library.yaml @@ -113,7 +113,8 @@ get: example: Firefly summary: type: string - example: Captain Malcolm 'Mal' Reynolds is a former galactic war veteran who is + example: + Captain Malcolm 'Mal' Reynolds is a former galactic war veteran who is the captain of the transport ship "Serenity". Mal and his crew, ensign Zoe Alleyne Washburne; Zoe's husband, pilot Hoban 'Wash' Washburne; muscular mercenary Jayne Cobb; young mechanic Kaylee diff --git a/src/paths/library/get-all-libraries.yaml b/src/paths/library/get-all-libraries.yaml index 34c8e9a6..2c621a43 100644 --- a/src/paths/library/get-all-libraries.yaml +++ b/src/paths/library/get-all-libraries.yaml @@ -60,7 +60,7 @@ get: example: /:/resources/movie.png key: type: string - example: '1' + example: "1" type: type: string example: movie @@ -80,11 +80,11 @@ get: type: string example: 322a231a-b7f7-49f5-920f-14c61199cd30 updatedAt: - $ref: '../../models/common/PlexDateTime.yaml' + $ref: "../../models/common/PlexDateTime.yaml" createdAt: - $ref: '../../models/common/PlexDateTime.yaml' + $ref: "../../models/common/PlexDateTime.yaml" scannedAt: - $ref: '../../models/common/PlexDateTime.yaml' + $ref: "../../models/common/PlexDateTime.yaml" content: type: boolean example: true diff --git a/src/paths/library/metadata-children.yaml b/src/paths/library/metadata-children.yaml index 03336b89..7cded1b9 100644 --- a/src/paths/library/metadata-children.yaml +++ b/src/paths/library/metadata-children.yaml @@ -16,7 +16,7 @@ get: description: | Adds additional elements to the response. Supported types are (Stream) in: query - schema: + schema: type: string required: false examples: diff --git a/src/paths/library/metadata/[ratingKey]/banner/get-banner-image.yaml b/src/paths/library/metadata/[ratingKey]/banner/get-banner-image.yaml index 7c71fc7f..70580de1 100644 --- a/src/paths/library/metadata/[ratingKey]/banner/get-banner-image.yaml +++ b/src/paths/library/metadata/[ratingKey]/banner/get-banner-image.yaml @@ -18,7 +18,7 @@ get: - $ref: "../../../../../parameters/image/upscale.yaml" - $ref: "../../../../../parameters/plex/x-plex-token.yaml" responses: - '200': + "200": description: Successful response returning an image headers: X-Plex-Protocol: diff --git a/src/paths/library/metadata/[ratingKey]/get-meta-data-by-rating-key.yaml b/src/paths/library/metadata/[ratingKey]/get-meta-data-by-rating-key.yaml index 3cf93af9..e23d67cc 100644 --- a/src/paths/library/metadata/[ratingKey]/get-meta-data-by-rating-key.yaml +++ b/src/paths/library/metadata/[ratingKey]/get-meta-data-by-rating-key.yaml @@ -89,7 +89,8 @@ get: example: PG-13 summary: type: string - example: Serenity continues the story of the TV series it was based upon + example: + Serenity continues the story of the TV series it was based upon ("Firefly"). River Tam had a secret - one in which she's not even aware - so dangerous, no one's safe, as an Alliance operative's sent to capture her, and all others are considered diff --git a/src/paths/library/metadata/[ratingKey]/thumb/get-thumb-image.yaml b/src/paths/library/metadata/[ratingKey]/thumb/get-thumb-image.yaml index 4765f22e..2484c60d 100644 --- a/src/paths/library/metadata/[ratingKey]/thumb/get-thumb-image.yaml +++ b/src/paths/library/metadata/[ratingKey]/thumb/get-thumb-image.yaml @@ -18,7 +18,7 @@ get: - $ref: "../../../../../parameters/image/upscale.yaml" - $ref: "../../../../../parameters/plex/x-plex-token.yaml" responses: - '200': + "200": description: Successful response returning an image headers: X-Plex-Protocol: diff --git a/src/paths/media/providers/get-media-providers.yaml b/src/paths/media/providers/get-media-providers.yaml index 4d01f805..486e6b47 100644 --- a/src/paths/media/providers/get-media-providers.yaml +++ b/src/paths/media/providers/get-media-providers.yaml @@ -5,10 +5,10 @@ get: description: Retrieves media providers and their features from the Plex server. operationId: get-media-providers parameters: - - $ref: '../../../parameters/accept-application-json.yaml' - - $ref: '../../../parameters/plex/x-plex-token.yaml' + - $ref: "../../../parameters/accept-application-json.yaml" + - $ref: "../../../parameters/plex/x-plex-token.yaml" responses: - '200': + "200": description: Media providers and their features content: application/json: diff --git a/src/paths/pins/pins-id.yaml b/src/paths/pins/pins-id.yaml index 865f8c78..588bd771 100644 --- a/src/paths/pins/pins-id.yaml +++ b/src/paths/pins/pins-id.yaml @@ -16,15 +16,15 @@ get: schema: type: integer responses: - '200': + "200": description: The Pin with a non-null authToken when it has been verified by the user content: application/json: schema: $ref: ../../models/AuthPinContainer.yaml - '400': + "400": $ref: ../../responses/400-MissingIdentifier.yaml - '404': + "404": description: Not Found or Expired content: application/json: diff --git a/src/paths/pins/pins.yaml b/src/paths/pins/pins.yaml index 83d4c837..47f7f2a8 100644 --- a/src/paths/pins/pins.yaml +++ b/src/paths/pins/pins.yaml @@ -1,6 +1,6 @@ post: servers: - - url: 'https://plex.tv/api/v2' + - url: "https://plex.tv/api/v2" tags: - Plex summary: Get a Pin @@ -21,12 +21,11 @@ post: - $ref: ../../parameters/plex/x-plex-identifier.yaml - $ref: ../../parameters/plex/x-plex-product.yaml responses: - '200': + "200": description: Requests a new pin id used in the authentication flow content: application/json: schema: $ref: ../../models/AuthPinContainer.yaml - '400': + "400": $ref: ../../responses/400-MissingIdentifier.yaml - diff --git a/src/paths/resources/resources.yaml b/src/paths/resources/resources.yaml index 281f9e3f..75b36ad4 100644 --- a/src/paths/resources/resources.yaml +++ b/src/paths/resources/resources.yaml @@ -1,6 +1,6 @@ get: servers: - - url: 'https://plex.tv/api/v2' + - url: "https://plex.tv/api/v2" security: [] tags: - Plex @@ -34,7 +34,7 @@ get: - 0 - 1 responses: - '200': + "200": description: List of Plex Devices. This includes Plex hosted servers and clients content: application/json: @@ -42,7 +42,7 @@ get: type: array items: $ref: ../../models/PlexDevice.yaml - '400': - $ref: '../../responses/400.yaml' - '401': - $ref: '../../responses/401.yaml' + "400": + $ref: "../../responses/400.yaml" + "401": + $ref: "../../responses/401.yaml" diff --git a/src/paths/statistics/resources.yaml b/src/paths/statistics/resources.yaml index ec313224..bfe6ea1a 100644 --- a/src/paths/statistics/resources.yaml +++ b/src/paths/statistics/resources.yaml @@ -34,10 +34,10 @@ get: items: type: object properties: - timespan: + timespan: type: integer example: 6 - at: + at: type: integer example: 1718384427 hostCpuUtilization: @@ -55,8 +55,8 @@ get: processMemoryUtilization: type: number format: float - example: 0.493 + example: 0.493 "400": - $ref: "../../responses/400.yaml" + $ref: "../../responses/400.yaml" "401": $ref: "../../responses/401.yaml" diff --git a/src/paths/status/session-history.yaml b/src/paths/status/session-history.yaml index 1b3215de..0e9093f3 100644 --- a/src/paths/status/session-history.yaml +++ b/src/paths/status/session-history.yaml @@ -4,7 +4,7 @@ get: summary: Get Session History description: This will Retrieve a listing of all history views. operationId: getSessionHistory - parameters: + parameters: - name: sort description: | Sorts the results by the specified field followed by the direction (asc, desc) @@ -12,7 +12,7 @@ get: schema: type: string required: false - examples: + examples: viewed-at-descending: value: viewedAt:desc viewed-at-ascending: @@ -25,7 +25,7 @@ get: description: | Filter results by those that are related to a specific users id in: query - schema: + schema: type: integer required: false example: 1 @@ -36,7 +36,7 @@ get: in: query schema: type: object - pattern: '^[A-Za-z][A-Za-z0-9]*[>=<]{0,2}$' + pattern: "^[A-Za-z][A-Za-z0-9]*[>=<]{0,2}$" example: viewed-at-greater-than: value: viewedAt> @@ -52,7 +52,7 @@ get: description: | Filters the results based on the id of a valid library section in: query - schema: + schema: type: integer required: false example: 12 diff --git a/src/paths/user/get-user-data-by-token.yaml b/src/paths/user/get-user-data-by-token.yaml index 3db6d00e..bfcf89c6 100644 --- a/src/paths/user/get-user-data-by-token.yaml +++ b/src/paths/user/get-user-data-by-token.yaml @@ -1,22 +1,22 @@ get: servers: - - url: 'https://plex.tv/api/v2' + - url: "https://plex.tv/api/v2" tags: - Authentication summary: Get User Data By Token description: Get the User data from the provided X-Plex-Token operationId: getUserDetails parameters: - - $ref: '../../parameters/plex/x-plex-token.yaml' + - $ref: "../../parameters/plex/x-plex-token.yaml" - $ref: "../../parameters/accept-application-json.yaml" responses: - '200': + "200": description: Logged in user details content: application/json: schema: $ref: ../../models/UserPlexAccount.yaml - '400': - $ref: '../../responses/400.yaml' - '401': - $ref: '../../responses/401.yaml' + "400": + $ref: "../../responses/400.yaml" + "401": + $ref: "../../responses/401.yaml" diff --git a/src/paths/users/post-sign-in.yaml b/src/paths/users/post-sign-in.yaml index d9dbf59a..f9874f42 100644 --- a/src/paths/users/post-sign-in.yaml +++ b/src/paths/users/post-sign-in.yaml @@ -1,6 +1,6 @@ post: servers: - - url: 'https://plex.tv/api/v2' + - url: "https://plex.tv/api/v2" security: [] tags: - Authentication @@ -31,7 +31,7 @@ post: default: false description: Login credentials responses: - '201': + "201": description: Returns the user account data with a valid auth token content: application/json: @@ -51,7 +51,7 @@ post: type: array items: type: object - '400': - $ref: '../../responses/400.yaml' - '401': - $ref: '../../responses/401.yaml' + "400": + $ref: "../../responses/400.yaml" + "401": + $ref: "../../responses/401.yaml" diff --git a/src/responses/400-MissingIdentifier.yaml b/src/responses/400-MissingIdentifier.yaml index 9bef8bdc..55efaf24 100644 --- a/src/responses/400-MissingIdentifier.yaml +++ b/src/responses/400-MissingIdentifier.yaml @@ -17,4 +17,4 @@ content: example: X-Plex-Client-Identifier is missing status: type: integer - example: 400 \ No newline at end of file + example: 400 diff --git a/src/responses/401.yaml b/src/responses/401.yaml index 69c4127a..0dc401e6 100644 --- a/src/responses/401.yaml +++ b/src/responses/401.yaml @@ -4,17 +4,17 @@ content: schema: type: object properties: - errors: + errors: type: array items: type: object - properties: - code: + properties: + code: type: number example: 1001 - message: + message: type: string example: User could not be authenticated - status: + status: type: number - example: 401 \ No newline at end of file + example: 401 diff --git a/tests/paths/library/[sectionId]/get-library-details.spec.ts b/tests/paths/library/[sectionId]/get-library-details.spec.ts index 5697c55a..6ce891ca 100644 --- a/tests/paths/library/[sectionId]/get-library-details.spec.ts +++ b/tests/paths/library/[sectionId]/get-library-details.spec.ts @@ -1,667 +1,722 @@ -import {validateResponseSpec} from "../../../utils"; -import {describe, it} from 'vitest' +import { validateResponseSpec } from "../../../utils" +import { describe, it } from "vitest" -describe('GET /library/sections', () => { - it('should validate the 200 response without includeDetails queryParam when the API spec is valid', () => { - const response = { - "MediaContainer": { - "size": 20, - "allowSync": false, - "art": "/:/resources/movie-fanart.jpg", - "content": "secondary", - "identifier": "com.plexapp.plugins.library", - "librarySectionID": 1, - "mediaTagPrefix": "/system/bundle/media/flags/", - "mediaTagVersion": 1724161316, - "thumb": "/:/resources/movie.png", - "title1": "Movies", - "viewGroup": "secondary", - "Directory": [{ - "key": "all", "title": "All Movies" - }, { - "key": "unwatched", "title": "Unwatched" - }, { - "key": "newest", "title": "Recently Released" - }, { - "key": "recentlyAdded", "title": "Recently Added" - }, { - "key": "recentlyViewed", "title": "Recently Viewed" - }, { - "key": "onDeck", "title": "Continue Watching" - }, { - "secondary": true, "key": "collection", "title": "By Collection" - }, { - "secondary": true, "key": "edition", "title": "By Edition" - }, { - "secondary": true, "key": "genre", "title": "By Genre" - }, { - "secondary": true, "key": "year", "title": "By Year" - }, { - "secondary": true, "key": "decade", "title": "By Decade" - }, { - "secondary": true, "key": "director", "title": "By Director" - }, { - "secondary": true, "key": "actor", "title": "By Starring Actor" - }, { - "secondary": true, "key": "country", "title": "By Country" - }, { - "secondary": true, "key": "contentRating", "title": "By Content Rating" - }, { - "secondary": true, "key": "rating", "title": "By Rating" - }, { - "secondary": true, "key": "resolution", "title": "By Resolution" - }, { - "secondary": true, "key": "firstCharacter", "title": "By First Letter" - }, { - "key": "folder", "title": "By Folder" - }, { - "prompt": "Search Movies", "search": true, "key": "search?type=1", "title": "Search..." - }] - } - }; +describe("GET /library/sections", () => { + it("should validate the 200 response without includeDetails queryParam when the API spec is valid", () => { + const response = { + MediaContainer: { + size: 20, + allowSync: false, + art: "/:/resources/movie-fanart.jpg", + content: "secondary", + identifier: "com.plexapp.plugins.library", + librarySectionID: 1, + mediaTagPrefix: "/system/bundle/media/flags/", + mediaTagVersion: 1724161316, + thumb: "/:/resources/movie.png", + title1: "Movies", + viewGroup: "secondary", + Directory: [ + { + key: "all", + title: "All Movies" + }, + { + key: "unwatched", + title: "Unwatched" + }, + { + key: "newest", + title: "Recently Released" + }, + { + key: "recentlyAdded", + title: "Recently Added" + }, + { + key: "recentlyViewed", + title: "Recently Viewed" + }, + { + key: "onDeck", + title: "Continue Watching" + }, + { + secondary: true, + key: "collection", + title: "By Collection" + }, + { + secondary: true, + key: "edition", + title: "By Edition" + }, + { + secondary: true, + key: "genre", + title: "By Genre" + }, + { + secondary: true, + key: "year", + title: "By Year" + }, + { + secondary: true, + key: "decade", + title: "By Decade" + }, + { + secondary: true, + key: "director", + title: "By Director" + }, + { + secondary: true, + key: "actor", + title: "By Starring Actor" + }, + { + secondary: true, + key: "country", + title: "By Country" + }, + { + secondary: true, + key: "contentRating", + title: "By Content Rating" + }, + { + secondary: true, + key: "rating", + title: "By Rating" + }, + { + secondary: true, + key: "resolution", + title: "By Resolution" + }, + { + secondary: true, + key: "firstCharacter", + title: "By First Letter" + }, + { + key: "folder", + title: "By Folder" + }, + { + prompt: "Search Movies", + search: true, + key: "search?type=1", + title: "Search..." + } + ] + } + } - validateResponseSpec("/library/sections/{sectionKey}", "get", 200, response) - }); + validateResponseSpec("/library/sections/{sectionKey}", "get", 200, response) + }) - it('should validate the 200 response with includeDetails queryParam when the API spec is valid', () => { - const response = { - "MediaContainer": { - "size": 29, - "allowSync": false, - "art": "/:/resources/movie-fanart.jpg", - "content": "secondary", - "identifier": "com.plexapp.plugins.library", - "librarySectionID": 1, - "mediaTagPrefix": "/system/bundle/media/flags/", - "mediaTagVersion": 1724161316, - "thumb": "/:/resources/movie.png", - "title1": "Movies", - "viewGroup": "secondary", - "Directory": [ - { - "key": "all", - "title": "All Movies" - }, - { - "key": "unwatched", - "title": "Unwatched" - }, - { - "key": "newest", - "title": "Recently Released" - }, - { - "key": "recentlyAdded", - "title": "Recently Added" - }, - { - "key": "recentlyViewed", - "title": "Recently Viewed" - }, - { - "key": "onDeck", - "title": "Continue Watching" - }, - { - "secondary": true, - "key": "collection", - "title": "By Collection" - }, - { - "secondary": true, - "key": "edition", - "title": "By Edition" - }, - { - "secondary": true, - "key": "genre", - "title": "By Genre" - }, - { - "secondary": true, - "key": "year", - "title": "By Year" - }, - { - "secondary": true, - "key": "decade", - "title": "By Decade" - }, - { - "secondary": true, - "key": "director", - "title": "By Director" - }, - { - "secondary": true, - "key": "actor", - "title": "By Starring Actor" - }, - { - "secondary": true, - "key": "country", - "title": "By Country" - }, - { - "secondary": true, - "key": "contentRating", - "title": "By Content Rating" - }, - { - "secondary": true, - "key": "rating", - "title": "By Rating" - }, - { - "secondary": true, - "key": "resolution", - "title": "By Resolution" - }, - { - "secondary": true, - "key": "firstCharacter", - "title": "By First Letter" - }, - { - "key": "folder", - "title": "By Folder" - }, - { - "prompt": "Search Movies", - "search": true, - "key": "search?type=1", - "title": "Search..." - } - ], - "Type": [ - { - "key": "/library/sections/1/all?type=1", - "type": "movie", - "title": "Movies", - "active": false, - "Filter": [ - { - "filter": "genre", - "filterType": "string", - "key": "/library/sections/1/genre", - "title": "Genre", - "type": "filter" - }, - { - "filter": "year", - "filterType": "integer", - "key": "/library/sections/1/year", - "title": "Year", - "type": "filter" - }, - { - "filter": "decade", - "filterType": "integer", - "key": "/library/sections/1/decade", - "title": "Decade", - "type": "filter" - }, - { - "filter": "contentRating", - "filterType": "string", - "key": "/library/sections/1/contentRating", - "title": "Content Rating", - "type": "filter" - }, - { - "filter": "collection", - "filterType": "string", - "key": "/library/sections/1/collection", - "title": "Collection", - "type": "filter" - }, - { - "filter": "director", - "filterType": "string", - "key": "/library/sections/1/director", - "title": "Director", - "type": "filter" - }, - { - "filter": "actor", - "filterType": "string", - "key": "/library/sections/1/actor", - "title": "Actor", - "type": "filter" - }, - { - "filter": "writer", - "filterType": "string", - "key": "/library/sections/1/writer", - "title": "Writer", - "type": "filter" - }, - { - "filter": "producer", - "filterType": "string", - "key": "/library/sections/1/producer", - "title": "Producer", - "type": "filter" - }, - { - "filter": "country", - "filterType": "string", - "key": "/library/sections/1/country", - "title": "Country", - "type": "filter" - }, - { - "filter": "studio", - "filterType": "string", - "key": "/library/sections/1/studio", - "title": "Studio", - "type": "filter" - }, - { - "filter": "resolution", - "filterType": "string", - "key": "/library/sections/1/resolution", - "title": "Resolution", - "type": "filter" - }, - { - "filter": "hdr", - "filterType": "boolean", - "key": "/library/sections/1/hdr", - "title": "HDR", - "type": "filter" - }, - { - "filter": "unwatched", - "filterType": "boolean", - "key": "/library/sections/1/unwatched", - "title": "Unwatched", - "type": "filter" - }, - { - "filter": "inProgress", - "filterType": "boolean", - "key": "/library/sections/1/inProgress", - "title": "In Progress", - "type": "filter" - }, - { - "filter": "unmatched", - "filterType": "boolean", - "key": "/library/sections/1/unmatched", - "title": "Unmatched", - "type": "filter" - }, - { - "filter": "audioLanguage", - "filterType": "string", - "key": "/library/sections/1/audioLanguage", - "title": "Audio Language", - "type": "filter" - }, - { - "filter": "subtitleLanguage", - "filterType": "string", - "key": "/library/sections/1/subtitleLanguage", - "title": "Subtitle Language", - "type": "filter" - }, - { - "filter": "label", - "filterType": "string", - "key": "/library/sections/1/label", - "title": "Labels", - "type": "filter" - } - ], - "Sort": [ - { - "default": "asc", - "defaultDirection": "asc", - "descKey": "titleSort:desc", - "firstCharacterKey": "/library/sections/1/firstCharacter", - "key": "titleSort", - "title": "Title" - }, - { - "defaultDirection": "desc", - "descKey": "originallyAvailableAt:desc", - "key": "originallyAvailableAt", - "title": "Release Date" - }, - { - "defaultDirection": "desc", - "descKey": "rating:desc", - "key": "rating", - "title": "Critic Rating" - }, - { - "defaultDirection": "desc", - "descKey": "audienceRating:desc", - "key": "audienceRating", - "title": "Audience Rating" - }, - { - "defaultDirection": "desc", - "descKey": "duration:desc", - "key": "duration", - "title": "Duration" - }, - { - "defaultDirection": "desc", - "descKey": "addedAt:desc", - "key": "addedAt", - "title": "Date Added" - }, - { - "defaultDirection": "desc", - "descKey": "lastViewedAt:desc", - "key": "lastViewedAt", - "title": "Date Viewed" - }, - { - "defaultDirection": "asc", - "descKey": "mediaHeight:desc", - "key": "mediaHeight", - "title": "Resolution" - }, - { - "defaultDirection": "desc", - "descKey": "random:desc", - "key": "random", - "title": "Randomly" - } - ], - "Field": [ - { - "key": "title", - "title": "Title", - "type": "string" - }, - { - "key": "studio", - "title": "Studio", - "type": "string" - }, - { - "key": "userRating", - "subType": "rating", - "title": "Rating", - "type": "integer" - }, - { - "key": "contentRating", - "title": "Content Rating", - "type": "tag" - }, - { - "key": "year", - "subType": "year", - "title": "Year", - "type": "integer" - }, - { - "key": "decade", - "subType": "decade", - "title": "Decade", - "type": "integer" - }, - { - "key": "originallyAvailableAt", - "title": "Release Date", - "type": "date" - }, - { - "key": "duration", - "subType": "duration", - "title": "Duration", - "type": "integer" - }, - { - "key": "unmatched", - "title": "Unmatched", - "type": "boolean" - }, - { - "key": "duplicate", - "title": "Duplicate", - "type": "boolean" - }, - { - "key": "genre", - "title": "Genre", - "type": "tag" - }, - { - "key": "collection", - "title": "Collection", - "type": "tag" - }, - { - "key": "director", - "title": "Director", - "type": "tag" - }, - { - "key": "writer", - "title": "Writer", - "type": "tag" - }, - { - "key": "producer", - "title": "Producer", - "type": "tag" - }, - { - "key": "actor", - "title": "Actor", - "type": "tag" - }, - { - "key": "country", - "title": "Country", - "type": "tag" - }, - { - "key": "addedAt", - "title": "Date Added", - "type": "date" - }, - { - "key": "viewCount", - "title": "Plays", - "type": "integer" - }, - { - "key": "lastViewedAt", - "title": "Last Watched", - "type": "date" - }, - { - "key": "unwatched", - "title": "Unwatched", - "type": "boolean" - }, - { - "key": "resolution", - "title": "Resolution", - "type": "resolution" - }, - { - "key": "hdr", - "subType": "hdr", - "title": "HDR", - "type": "boolean" - }, - { - "key": "mediaSize", - "subType": "fileSize", - "title": "File Size", - "type": "integer" - }, - { - "key": "mediaBitrate", - "subType": "bitrate", - "title": "Bitrate", - "type": "integer" - }, - { - "key": "subtitleLanguage", - "title": "Subtitle Language", - "type": "subtitleLanguage" - }, - { - "key": "audioLanguage", - "title": "Audio Language", - "type": "audioLanguage" - }, - { - "key": "inProgress", - "title": "In Progress", - "type": "boolean" - }, - { - "key": "trash", - "title": "Trash", - "type": "boolean" - }, - { - "key": "label", - "title": "Label", - "type": "tag" - } - ] - } - ], - "FieldType": [ - { - "type": "tag", - "Operator": [ - { - "key": "=", - "title": "is" - }, - { - "key": "!=", - "title": "is not" - } - ] - }, - { - "type": "integer", - "Operator": [ - { - "key": "=", - "title": "is" - }, - { - "key": "!=", - "title": "is not" - }, - { - "key": ">>=", - "title": "is greater than" - }, - { - "key": "<<=", - "title": "is less than" - } - ] - }, - { - "type": "string", - "Operator": [ - { - "key": "=", - "title": "contains" - }, - { - "key": "!=", - "title": "does not contain" - }, - { - "key": "==", - "title": "is" - }, - { - "key": "!==", - "title": "is not" - }, - { - "key": "<=", - "title": "begins with" - }, - { - "key": ">=", - "title": "ends with" - } - ] - }, - { - "type": "boolean", - "Operator": [ - { - "key": "=", - "title": "is true" - }, - { - "key": "!=", - "title": "is false" - } - ] - }, - { - "type": "date", - "Operator": [ - { - "key": "<<=", - "title": "is before" - }, - { - "key": ">>=", - "title": "is after" - } - ] - }, - { - "type": "subtitleLanguage", - "Operator": [ - { - "key": "=", - "title": "is" - }, - { - "key": "!=", - "title": "is not" - } - ] - }, - { - "type": "audioLanguage", - "Operator": [ - { - "key": "=", - "title": "is" - }, - { - "key": "!=", - "title": "is not" - } - ] - }, - { - "type": "resolution", - "Operator": [ - { - "key": "=", - "title": "is" - } - ] - } - ] - } - }; + it("should validate the 200 response with includeDetails queryParam when the API spec is valid", () => { + const response = { + MediaContainer: { + size: 29, + allowSync: false, + art: "/:/resources/movie-fanart.jpg", + content: "secondary", + identifier: "com.plexapp.plugins.library", + librarySectionID: 1, + mediaTagPrefix: "/system/bundle/media/flags/", + mediaTagVersion: 1724161316, + thumb: "/:/resources/movie.png", + title1: "Movies", + viewGroup: "secondary", + Directory: [ + { + key: "all", + title: "All Movies" + }, + { + key: "unwatched", + title: "Unwatched" + }, + { + key: "newest", + title: "Recently Released" + }, + { + key: "recentlyAdded", + title: "Recently Added" + }, + { + key: "recentlyViewed", + title: "Recently Viewed" + }, + { + key: "onDeck", + title: "Continue Watching" + }, + { + secondary: true, + key: "collection", + title: "By Collection" + }, + { + secondary: true, + key: "edition", + title: "By Edition" + }, + { + secondary: true, + key: "genre", + title: "By Genre" + }, + { + secondary: true, + key: "year", + title: "By Year" + }, + { + secondary: true, + key: "decade", + title: "By Decade" + }, + { + secondary: true, + key: "director", + title: "By Director" + }, + { + secondary: true, + key: "actor", + title: "By Starring Actor" + }, + { + secondary: true, + key: "country", + title: "By Country" + }, + { + secondary: true, + key: "contentRating", + title: "By Content Rating" + }, + { + secondary: true, + key: "rating", + title: "By Rating" + }, + { + secondary: true, + key: "resolution", + title: "By Resolution" + }, + { + secondary: true, + key: "firstCharacter", + title: "By First Letter" + }, + { + key: "folder", + title: "By Folder" + }, + { + prompt: "Search Movies", + search: true, + key: "search?type=1", + title: "Search..." + } + ], + Type: [ + { + key: "/library/sections/1/all?type=1", + type: "movie", + title: "Movies", + active: false, + Filter: [ + { + filter: "genre", + filterType: "string", + key: "/library/sections/1/genre", + title: "Genre", + type: "filter" + }, + { + filter: "year", + filterType: "integer", + key: "/library/sections/1/year", + title: "Year", + type: "filter" + }, + { + filter: "decade", + filterType: "integer", + key: "/library/sections/1/decade", + title: "Decade", + type: "filter" + }, + { + filter: "contentRating", + filterType: "string", + key: "/library/sections/1/contentRating", + title: "Content Rating", + type: "filter" + }, + { + filter: "collection", + filterType: "string", + key: "/library/sections/1/collection", + title: "Collection", + type: "filter" + }, + { + filter: "director", + filterType: "string", + key: "/library/sections/1/director", + title: "Director", + type: "filter" + }, + { + filter: "actor", + filterType: "string", + key: "/library/sections/1/actor", + title: "Actor", + type: "filter" + }, + { + filter: "writer", + filterType: "string", + key: "/library/sections/1/writer", + title: "Writer", + type: "filter" + }, + { + filter: "producer", + filterType: "string", + key: "/library/sections/1/producer", + title: "Producer", + type: "filter" + }, + { + filter: "country", + filterType: "string", + key: "/library/sections/1/country", + title: "Country", + type: "filter" + }, + { + filter: "studio", + filterType: "string", + key: "/library/sections/1/studio", + title: "Studio", + type: "filter" + }, + { + filter: "resolution", + filterType: "string", + key: "/library/sections/1/resolution", + title: "Resolution", + type: "filter" + }, + { + filter: "hdr", + filterType: "boolean", + key: "/library/sections/1/hdr", + title: "HDR", + type: "filter" + }, + { + filter: "unwatched", + filterType: "boolean", + key: "/library/sections/1/unwatched", + title: "Unwatched", + type: "filter" + }, + { + filter: "inProgress", + filterType: "boolean", + key: "/library/sections/1/inProgress", + title: "In Progress", + type: "filter" + }, + { + filter: "unmatched", + filterType: "boolean", + key: "/library/sections/1/unmatched", + title: "Unmatched", + type: "filter" + }, + { + filter: "audioLanguage", + filterType: "string", + key: "/library/sections/1/audioLanguage", + title: "Audio Language", + type: "filter" + }, + { + filter: "subtitleLanguage", + filterType: "string", + key: "/library/sections/1/subtitleLanguage", + title: "Subtitle Language", + type: "filter" + }, + { + filter: "label", + filterType: "string", + key: "/library/sections/1/label", + title: "Labels", + type: "filter" + } + ], + Sort: [ + { + default: "asc", + defaultDirection: "asc", + descKey: "titleSort:desc", + firstCharacterKey: "/library/sections/1/firstCharacter", + key: "titleSort", + title: "Title" + }, + { + defaultDirection: "desc", + descKey: "originallyAvailableAt:desc", + key: "originallyAvailableAt", + title: "Release Date" + }, + { + defaultDirection: "desc", + descKey: "rating:desc", + key: "rating", + title: "Critic Rating" + }, + { + defaultDirection: "desc", + descKey: "audienceRating:desc", + key: "audienceRating", + title: "Audience Rating" + }, + { + defaultDirection: "desc", + descKey: "duration:desc", + key: "duration", + title: "Duration" + }, + { + defaultDirection: "desc", + descKey: "addedAt:desc", + key: "addedAt", + title: "Date Added" + }, + { + defaultDirection: "desc", + descKey: "lastViewedAt:desc", + key: "lastViewedAt", + title: "Date Viewed" + }, + { + defaultDirection: "asc", + descKey: "mediaHeight:desc", + key: "mediaHeight", + title: "Resolution" + }, + { + defaultDirection: "desc", + descKey: "random:desc", + key: "random", + title: "Randomly" + } + ], + Field: [ + { + key: "title", + title: "Title", + type: "string" + }, + { + key: "studio", + title: "Studio", + type: "string" + }, + { + key: "userRating", + subType: "rating", + title: "Rating", + type: "integer" + }, + { + key: "contentRating", + title: "Content Rating", + type: "tag" + }, + { + key: "year", + subType: "year", + title: "Year", + type: "integer" + }, + { + key: "decade", + subType: "decade", + title: "Decade", + type: "integer" + }, + { + key: "originallyAvailableAt", + title: "Release Date", + type: "date" + }, + { + key: "duration", + subType: "duration", + title: "Duration", + type: "integer" + }, + { + key: "unmatched", + title: "Unmatched", + type: "boolean" + }, + { + key: "duplicate", + title: "Duplicate", + type: "boolean" + }, + { + key: "genre", + title: "Genre", + type: "tag" + }, + { + key: "collection", + title: "Collection", + type: "tag" + }, + { + key: "director", + title: "Director", + type: "tag" + }, + { + key: "writer", + title: "Writer", + type: "tag" + }, + { + key: "producer", + title: "Producer", + type: "tag" + }, + { + key: "actor", + title: "Actor", + type: "tag" + }, + { + key: "country", + title: "Country", + type: "tag" + }, + { + key: "addedAt", + title: "Date Added", + type: "date" + }, + { + key: "viewCount", + title: "Plays", + type: "integer" + }, + { + key: "lastViewedAt", + title: "Last Watched", + type: "date" + }, + { + key: "unwatched", + title: "Unwatched", + type: "boolean" + }, + { + key: "resolution", + title: "Resolution", + type: "resolution" + }, + { + key: "hdr", + subType: "hdr", + title: "HDR", + type: "boolean" + }, + { + key: "mediaSize", + subType: "fileSize", + title: "File Size", + type: "integer" + }, + { + key: "mediaBitrate", + subType: "bitrate", + title: "Bitrate", + type: "integer" + }, + { + key: "subtitleLanguage", + title: "Subtitle Language", + type: "subtitleLanguage" + }, + { + key: "audioLanguage", + title: "Audio Language", + type: "audioLanguage" + }, + { + key: "inProgress", + title: "In Progress", + type: "boolean" + }, + { + key: "trash", + title: "Trash", + type: "boolean" + }, + { + key: "label", + title: "Label", + type: "tag" + } + ] + } + ], + FieldType: [ + { + type: "tag", + Operator: [ + { + key: "=", + title: "is" + }, + { + key: "!=", + title: "is not" + } + ] + }, + { + type: "integer", + Operator: [ + { + key: "=", + title: "is" + }, + { + key: "!=", + title: "is not" + }, + { + key: ">>=", + title: "is greater than" + }, + { + key: "<<=", + title: "is less than" + } + ] + }, + { + type: "string", + Operator: [ + { + key: "=", + title: "contains" + }, + { + key: "!=", + title: "does not contain" + }, + { + key: "==", + title: "is" + }, + { + key: "!==", + title: "is not" + }, + { + key: "<=", + title: "begins with" + }, + { + key: ">=", + title: "ends with" + } + ] + }, + { + type: "boolean", + Operator: [ + { + key: "=", + title: "is true" + }, + { + key: "!=", + title: "is false" + } + ] + }, + { + type: "date", + Operator: [ + { + key: "<<=", + title: "is before" + }, + { + key: ">>=", + title: "is after" + } + ] + }, + { + type: "subtitleLanguage", + Operator: [ + { + key: "=", + title: "is" + }, + { + key: "!=", + title: "is not" + } + ] + }, + { + type: "audioLanguage", + Operator: [ + { + key: "=", + title: "is" + }, + { + key: "!=", + title: "is not" + } + ] + }, + { + type: "resolution", + Operator: [ + { + key: "=", + title: "is" + } + ] + } + ] + } + } - validateResponseSpec("/library/sections/{sectionKey}", "get", 200, response) - }); + validateResponseSpec("/library/sections/{sectionKey}", "get", 200, response) + }) }) diff --git a/tests/paths/library/[sectionId]/get-library-items.spec.ts b/tests/paths/library/[sectionId]/get-library-items.spec.ts index 50521314..81680f97 100644 --- a/tests/paths/library/[sectionId]/get-library-items.spec.ts +++ b/tests/paths/library/[sectionId]/get-library-items.spec.ts @@ -1,1146 +1,1616 @@ -import {validateResponseSpec} from "../../../utils"; -import {describe, it} from 'vitest' +import { validateResponseSpec } from "../../../utils" +import { describe, it } from "vitest" -describe('GET /library/sections', () => { - it('should validate the 200 response without includeGuids and includeMeta queryParam when the API spec is valid', () => { - // Response from the API endpoint - // {{baseUrl}}/library/sections/1/all?X-Plex-Container-Start=1&X-Plex-Container-Size=5 - const response = { - "MediaContainer": { - "size": 5, - "totalSize": 357, - "offset": 1, - "allowSync": true, - "art": "/:/resources/movie-fanart.jpg", - "content": "secondary", - "identifier": "com.plexapp.plugins.library", - "librarySectionID": 1, - "librarySectionTitle": "Movies", - "librarySectionUUID": "0e71027d-88bc-4413-9927-5aad992d3d19", - "mediaTagPrefix": "/system/bundle/media/flags/", - "mediaTagVersion": 1724161316, - "thumb": "/:/resources/movie.png", - "title1": "Movies", - "title2": "All Movies", - "viewGroup": "movie", - "Metadata": [{ - "ratingKey": "9881", - "key": "/library/metadata/9881", - "guid": "plex://movie/5d776834961905001eb939ac", - "slug": "91-2-weeks", - "studio": "Jonesfilm", - "type": "movie", - "title": "9½ Weeks", - "originalTitle": "Nine 1/2 Weeks", - "contentRating": "nl/12", - "summary": "An erotic story about a woman, the assistant of an art gallery, who gets involved in an impersonal affair with a man. She barely knows about his life, only about the sex games they play, so the relationship begins to get complicated.", - "rating": 6.0, - "audienceRating": 5.6, - "year": 1986, - "tagline": "They Broke Every Rule.", - "thumb": "/library/metadata/9881/thumb/1723182007", - "art": "/library/metadata/9881/art/1723182007", - "duration": 7011007, - "originallyAvailableAt": "1986-02-09", - "addedAt": 1560801795, - "updatedAt": 1723182007, - "audienceRatingImage": "rottentomatoes://image.rating.spilled", - "chapterSource": "media", - "ratingImage": "rottentomatoes://image.rating.ripe", - "Media": [{ - "id": 12306, - "duration": 7011007, - "bitrate": 13003, - "width": 1920, - "height": 1080, - "aspectRatio": 1.78, - "audioChannels": 6, - "audioCodec": "dca", - "videoCodec": "h264", - "videoResolution": "1080", - "container": "mkv", - "videoFrameRate": "24p", - "audioProfile": "dts", - "videoProfile": "high", - "Part": [{ - "id": 41930, - "key": "/library/parts/41930/1501053187/file.mkv", - "duration": 7011007, - "file": "/Movies/Nine 1+2 Weeks (1986)/Nine.and.a.Half.Weeks.1986.1080p.BluRay.DTS.x264-HDMaNiAcS.mkv", - "size": 11394952434, - "audioProfile": "dts", - "container": "mkv", - "indexes": "sd", - "videoProfile": "high" - }] - }], - "UltraBlurColors": { - "topLeft": "452410", "topRight": "0d0202", "bottomRight": "0d0202", "bottomLeft": "272223" - }, - "Genre": [{ - "tag": "Drama" - }, { - "tag": "Romance" - }], - "Country": [{ - "tag": "United States of America" - }], - "Director": [{ - "tag": "Adrian Lyne" - }], - "Writer": [{ - "tag": "Sarah Kernochan" - }, { - "tag": "Zalman King" - }], - "Role": [{ - "tag": "Mickey Rourke" - }, { - "tag": "Kim Basinger" - }, { - "tag": "Margaret Whitton" - }] - }, { - "ratingKey": "20936", - "key": "/library/metadata/20936", - "guid": "plex://movie/5d776831a091de001f2e7756", - "slug": "10000-bc", - "studio": "Centropolis Entertainment", - "type": "movie", - "title": "10,000 BC", - "contentRating": "nl/12", - "summary": "In the prehistoric past, D'Leh is a mammoth hunter who bonds with the beautiful Evolet. When warriors on horseback capture Evolet and the tribesmen, D'Leh must embark on an odyssey to save his true love.", - "rating": 0.9, - "audienceRating": 3.7, - "viewOffset": 3866000, - "lastViewedAt": 1563831721, - "year": 2008, - "tagline": "The legend. The battle. The first hero.", - "thumb": "/library/metadata/20936/thumb/1721967792", - "art": "/library/metadata/20936/art/1721967792", - "duration": 6535680, - "originallyAvailableAt": "2008-03-05", - "addedAt": 1558940057, - "updatedAt": 1721967792, - "audienceRatingImage": "rottentomatoes://image.rating.spilled", - "chapterSource": "media", - "ratingImage": "rottentomatoes://image.rating.rotten", - "Media": [{ - "id": 21335, - "duration": 6535680, - "bitrate": 18740, - "width": 1920, - "height": 1080, - "aspectRatio": 1.78, - "audioChannels": 6, - "audioCodec": "truehd", - "videoCodec": "vc1", - "videoResolution": "1080", - "container": "mkv", - "videoFrameRate": "24p", - "videoProfile": "advanced", - "Part": [{ - "id": 41744, - "key": "/library/parts/41744/1563131036/file.mkv", - "duration": 6535680, - "file": "/Movies/10,000 BC (2008)/10000.BC.2008.1080p.BluRay.REMUX.VC-1.TrueHD.5.1-EPSiLON.mkv", - "size": 15359810466, - "container": "mkv", - "indexes": "sd", - "videoProfile": "advanced" - }] - }], - "UltraBlurColors": { - "topLeft": "173524", "topRight": "8e471e", "bottomRight": "734124", "bottomLeft": "864e20" - }, - "Genre": [{ - "tag": "Action" - }, { - "tag": "Adventure" - }], - "Country": [{ - "tag": "United States of America" - }, { - "tag": "South Africa" - }], - "Collection": [{ - "tag": "Working NL Subs" - }], - "Director": [{ - "tag": "Roland Emmerich" - }], - "Writer": [{ - "tag": "Harald Kloser" - }, { - "tag": "Roland Emmerich" - }], - "Role": [{ - "tag": "Steven Strait" - }, { - "tag": "Camilla Belle" - }, { - "tag": "Cliff Curtis" - }] - }, { - "ratingKey": "47175", - "key": "/library/metadata/47175", - "guid": "plex://movie/5d776d6c23d5a3001f5230e4", - "slug": "22-july", - "studio": "Scott Rudin Productions", - "type": "movie", - "title": "22 July", - "contentRating": "nl/16", - "summary": "A three-part story of Norway's worst terrorist attack in which over seventy people were killed. 22 July looks at the disaster itself, the survivors, Norway's political system and the lawyers who worked on this horrific case.", - "rating": 8.1, - "audienceRating": 7.0, - "year": 2018, - "tagline": "The true story of a day that started like any other", - "thumb": "/library/metadata/47175/thumb/1721705174", - "art": "/library/metadata/47175/art/1721705174", - "duration": 8640416, - "originallyAvailableAt": "2018-10-04", - "addedAt": 1657821161, - "updatedAt": 1721705174, - "audienceRatingImage": "rottentomatoes://image.rating.upright", - "ratingImage": "rottentomatoes://image.rating.ripe", - "Media": [{ - "id": 50492, - "duration": 8640416, - "bitrate": 15942, - "width": 3840, - "height": 2160, - "aspectRatio": 1.78, - "audioChannels": 6, - "audioCodec": "eac3", - "videoCodec": "hevc", - "videoResolution": "4k", - "container": "mkv", - "videoFrameRate": "24p", - "videoProfile": "main 10", - "Part": [{ - "id": 79867, - "key": "/library/parts/79867/1595149163/file.mkv", - "duration": 8640416, - "file": "/Movies/22 July (2018)/22 July (2018) WEBDL-2160p.mkv", - "size": 17221974810, - "container": "mkv", - "indexes": "sd", - "videoProfile": "main 10" - }] - }], - "UltraBlurColors": { - "topLeft": "243032", "topRight": "245968", "bottomRight": "113140", "bottomLeft": "1b1d1f" - }, - "Genre": [{ - "tag": "Drama" - }, { - "tag": "History" - }], - "Country": [{ - "tag": "Iceland" - }, { - "tag": "Norway" - }], - "Director": [{ - "tag": "Paul Greengrass" - }], - "Writer": [{ - "tag": "Åsne Seierstad" - }, { - "tag": "Paul Greengrass" - }], - "Role": [{ - "tag": "Jonas Strand Gravli" - }, { - "tag": "Anders Danielsen Lie" - }, { - "tag": "Jon Øigarden" - }] - }, { - "ratingKey": "21488", - "key": "/library/metadata/21488", - "guid": "plex://movie/5d776bd9fb0d55001f574e73", - "slug": "the-24-hour-war", - "studio": "Chassy Media", - "type": "movie", - "title": "The 24 Hour War", - "titleSort": "24 Hour War", - "summary": "In the early 1960s, Henry Ford II and Enzo Ferrari went to war on the battlefield of Le Mans(TM). This epic battle saw drivers lose their lives, family dynasties nearly collapse and the development of a new race car that changed racing.", - "rating": 10.0, - "audienceRating": 8.6, - "skipCount": 1, - "year": 2016, - "thumb": "/library/metadata/21488/thumb/1721967875", - "art": "/library/metadata/21488/art/1721967875", - "duration": 5997792, - "originallyAvailableAt": "2016-11-22", - "addedAt": 1565072391, - "updatedAt": 1721967875, - "audienceRatingImage": "rottentomatoes://image.rating.upright", - "chapterSource": "media", - "ratingImage": "rottentomatoes://image.rating.ripe", - "Media": [{ - "id": 21822, - "duration": 5997792, - "bitrate": 11730, - "width": 1920, - "height": 1080, - "aspectRatio": 1.78, - "audioChannels": 6, - "audioCodec": "ac3", - "videoCodec": "h264", - "videoResolution": "1080", - "container": "mkv", - "videoFrameRate": "24p", - "videoProfile": "high", - "Part": [{ - "id": 41819, - "key": "/library/parts/41819/1492372630/file.mkv", - "duration": 5997792, - "file": "/Movies/The 24 Hour War (2016)/The.24.Hour.War.2016.1080p.BluRay.DD5.1.x264-HiFi.mkv", - "size": 8796183796, - "container": "mkv", - "indexes": "sd", - "videoProfile": "high" - }] - }], - "UltraBlurColors": { - "topLeft": "511811", "topRight": "692a1e", "bottomRight": "2d0f0b", "bottomLeft": "551c17" - }, - "Genre": [{ - "tag": "Documentary" - }, { - "tag": "History" - }], - "Country": [{ - "tag": "United States of America" - }], - "Director": [{ - "tag": "Adam Carolla" - }, { - "tag": "Nate Adams" - }], - "Role": [{ - "tag": "Mario Andretti" - }, { - "tag": "Bob Bondurant" - }, { - "tag": "Ralph Nader" - }] - }, { - "ratingKey": "21050", - "key": "/library/metadata/21050", - "guid": "plex://movie/5d776824f54112001f5bbdd7", - "slug": "28-days-later", - "studio": "DNA Films", - "type": "movie", - "title": "28 Days Later", - "originalTitle": "28 Days Later...", - "contentRating": "nl/16", - "summary": "Twenty-eight days after a killer virus was accidentally unleashed from a British research facility, a small group of London survivors are caught in a desperate struggle to protect themselves from the infected. Carried by animals and humans, the virus turns those it infects into homicidal maniacs -- and it's absolutely impossible to contain.", - "rating": 8.7, - "audienceRating": 8.5, - "viewOffset": 3421000, - "lastViewedAt": 1554844099, - "year": 2002, - "tagline": "His fear began when he woke up alone. His terror began when he realised he wasn't.", - "thumb": "/library/metadata/21050/thumb/1721967834", - "art": "/library/metadata/21050/art/1721967834", - "duration": 6787605, - "originallyAvailableAt": "2002-11-01", - "addedAt": 1563846686, - "updatedAt": 1721967834, - "audienceRatingImage": "rottentomatoes://image.rating.upright", - "ratingImage": "rottentomatoes://image.rating.ripe", - "Media": [{ - "id": 21442, - "duration": 6787605, - "bitrate": 10056, - "width": 1920, - "height": 1040, - "aspectRatio": 1.85, - "audioChannels": 6, - "audioCodec": "dca", - "videoCodec": "h264", - "videoResolution": "1080", - "container": "mkv", - "videoFrameRate": "24p", - "audioProfile": "dts", - "videoProfile": "high", - "Part": [{ - "id": 41881, - "key": "/library/parts/41881/1540562456/file.mkv", - "duration": 6787605, - "file": "/Movies/28 Days Later (2002)/28.Days.Later.2002.1080p.REPACK.BluRay.x264-AVCHD.mkv", - "size": 8534411908, - "audioProfile": "dts", - "container": "mkv", - "indexes": "sd", - "videoProfile": "high" - }] - }], - "UltraBlurColors": { - "topLeft": "571002", "topRight": "6e190f", "bottomRight": "9f1a03", "bottomLeft": "430b04" - }, - "Genre": [{ - "tag": "Horror" - }, { - "tag": "Thriller" - }], - "Country": [{ - "tag": "United Kingdom" - }], - "Director": [{ - "tag": "Danny Boyle" - }], - "Writer": [{ - "tag": "Alex Garland" - }], - "Role": [{ - "tag": "Cillian Murphy" - }, { - "tag": "Naomie Harris" - }, { - "tag": "Brendan Gleeson" - }] - }] - } - }; +describe("GET /library/sections", () => { + it("should validate the 200 response without includeGuids and includeMeta queryParam when the API spec is valid", () => { + // Response from the API endpoint + // {{baseUrl}}/library/sections/1/all?X-Plex-Container-Start=1&X-Plex-Container-Size=5 + const response = { + MediaContainer: { + size: 5, + totalSize: 357, + offset: 1, + allowSync: true, + art: "/:/resources/movie-fanart.jpg", + content: "secondary", + identifier: "com.plexapp.plugins.library", + librarySectionID: 1, + librarySectionTitle: "Movies", + librarySectionUUID: "0e71027d-88bc-4413-9927-5aad992d3d19", + mediaTagPrefix: "/system/bundle/media/flags/", + mediaTagVersion: 1724161316, + thumb: "/:/resources/movie.png", + title1: "Movies", + title2: "All Movies", + viewGroup: "movie", + Metadata: [ + { + ratingKey: "9881", + key: "/library/metadata/9881", + guid: "plex://movie/5d776834961905001eb939ac", + slug: "91-2-weeks", + studio: "Jonesfilm", + type: "movie", + title: "9½ Weeks", + originalTitle: "Nine 1/2 Weeks", + contentRating: "nl/12", + summary: + "An erotic story about a woman, the assistant of an art gallery, who gets involved in an impersonal affair with a man. She barely knows about his life, only about the sex games they play, so the relationship begins to get complicated.", + rating: 6.0, + audienceRating: 5.6, + year: 1986, + tagline: "They Broke Every Rule.", + thumb: "/library/metadata/9881/thumb/1723182007", + art: "/library/metadata/9881/art/1723182007", + duration: 7011007, + originallyAvailableAt: "1986-02-09", + addedAt: 1560801795, + updatedAt: 1723182007, + audienceRatingImage: "rottentomatoes://image.rating.spilled", + chapterSource: "media", + ratingImage: "rottentomatoes://image.rating.ripe", + Media: [ + { + id: 12306, + duration: 7011007, + bitrate: 13003, + width: 1920, + height: 1080, + aspectRatio: 1.78, + audioChannels: 6, + audioCodec: "dca", + videoCodec: "h264", + videoResolution: "1080", + container: "mkv", + videoFrameRate: "24p", + audioProfile: "dts", + videoProfile: "high", + Part: [ + { + id: 41930, + key: "/library/parts/41930/1501053187/file.mkv", + duration: 7011007, + file: "/Movies/Nine 1+2 Weeks (1986)/Nine.and.a.Half.Weeks.1986.1080p.BluRay.DTS.x264-HDMaNiAcS.mkv", + size: 11394952434, + audioProfile: "dts", + container: "mkv", + indexes: "sd", + videoProfile: "high" + } + ] + } + ], + UltraBlurColors: { + topLeft: "452410", + topRight: "0d0202", + bottomRight: "0d0202", + bottomLeft: "272223" + }, + Genre: [ + { + tag: "Drama" + }, + { + tag: "Romance" + } + ], + Country: [ + { + tag: "United States of America" + } + ], + Director: [ + { + tag: "Adrian Lyne" + } + ], + Writer: [ + { + tag: "Sarah Kernochan" + }, + { + tag: "Zalman King" + } + ], + Role: [ + { + tag: "Mickey Rourke" + }, + { + tag: "Kim Basinger" + }, + { + tag: "Margaret Whitton" + } + ] + }, + { + ratingKey: "20936", + key: "/library/metadata/20936", + guid: "plex://movie/5d776831a091de001f2e7756", + slug: "10000-bc", + studio: "Centropolis Entertainment", + type: "movie", + title: "10,000 BC", + contentRating: "nl/12", + summary: + "In the prehistoric past, D'Leh is a mammoth hunter who bonds with the beautiful Evolet. When warriors on horseback capture Evolet and the tribesmen, D'Leh must embark on an odyssey to save his true love.", + rating: 0.9, + audienceRating: 3.7, + viewOffset: 3866000, + lastViewedAt: 1563831721, + year: 2008, + tagline: "The legend. The battle. The first hero.", + thumb: "/library/metadata/20936/thumb/1721967792", + art: "/library/metadata/20936/art/1721967792", + duration: 6535680, + originallyAvailableAt: "2008-03-05", + addedAt: 1558940057, + updatedAt: 1721967792, + audienceRatingImage: "rottentomatoes://image.rating.spilled", + chapterSource: "media", + ratingImage: "rottentomatoes://image.rating.rotten", + Media: [ + { + id: 21335, + duration: 6535680, + bitrate: 18740, + width: 1920, + height: 1080, + aspectRatio: 1.78, + audioChannels: 6, + audioCodec: "truehd", + videoCodec: "vc1", + videoResolution: "1080", + container: "mkv", + videoFrameRate: "24p", + videoProfile: "advanced", + Part: [ + { + id: 41744, + key: "/library/parts/41744/1563131036/file.mkv", + duration: 6535680, + file: "/Movies/10,000 BC (2008)/10000.BC.2008.1080p.BluRay.REMUX.VC-1.TrueHD.5.1-EPSiLON.mkv", + size: 15359810466, + container: "mkv", + indexes: "sd", + videoProfile: "advanced" + } + ] + } + ], + UltraBlurColors: { + topLeft: "173524", + topRight: "8e471e", + bottomRight: "734124", + bottomLeft: "864e20" + }, + Genre: [ + { + tag: "Action" + }, + { + tag: "Adventure" + } + ], + Country: [ + { + tag: "United States of America" + }, + { + tag: "South Africa" + } + ], + Collection: [ + { + tag: "Working NL Subs" + } + ], + Director: [ + { + tag: "Roland Emmerich" + } + ], + Writer: [ + { + tag: "Harald Kloser" + }, + { + tag: "Roland Emmerich" + } + ], + Role: [ + { + tag: "Steven Strait" + }, + { + tag: "Camilla Belle" + }, + { + tag: "Cliff Curtis" + } + ] + }, + { + ratingKey: "47175", + key: "/library/metadata/47175", + guid: "plex://movie/5d776d6c23d5a3001f5230e4", + slug: "22-july", + studio: "Scott Rudin Productions", + type: "movie", + title: "22 July", + contentRating: "nl/16", + summary: + "A three-part story of Norway's worst terrorist attack in which over seventy people were killed. 22 July looks at the disaster itself, the survivors, Norway's political system and the lawyers who worked on this horrific case.", + rating: 8.1, + audienceRating: 7.0, + year: 2018, + tagline: "The true story of a day that started like any other", + thumb: "/library/metadata/47175/thumb/1721705174", + art: "/library/metadata/47175/art/1721705174", + duration: 8640416, + originallyAvailableAt: "2018-10-04", + addedAt: 1657821161, + updatedAt: 1721705174, + audienceRatingImage: "rottentomatoes://image.rating.upright", + ratingImage: "rottentomatoes://image.rating.ripe", + Media: [ + { + id: 50492, + duration: 8640416, + bitrate: 15942, + width: 3840, + height: 2160, + aspectRatio: 1.78, + audioChannels: 6, + audioCodec: "eac3", + videoCodec: "hevc", + videoResolution: "4k", + container: "mkv", + videoFrameRate: "24p", + videoProfile: "main 10", + Part: [ + { + id: 79867, + key: "/library/parts/79867/1595149163/file.mkv", + duration: 8640416, + file: "/Movies/22 July (2018)/22 July (2018) WEBDL-2160p.mkv", + size: 17221974810, + container: "mkv", + indexes: "sd", + videoProfile: "main 10" + } + ] + } + ], + UltraBlurColors: { + topLeft: "243032", + topRight: "245968", + bottomRight: "113140", + bottomLeft: "1b1d1f" + }, + Genre: [ + { + tag: "Drama" + }, + { + tag: "History" + } + ], + Country: [ + { + tag: "Iceland" + }, + { + tag: "Norway" + } + ], + Director: [ + { + tag: "Paul Greengrass" + } + ], + Writer: [ + { + tag: "Åsne Seierstad" + }, + { + tag: "Paul Greengrass" + } + ], + Role: [ + { + tag: "Jonas Strand Gravli" + }, + { + tag: "Anders Danielsen Lie" + }, + { + tag: "Jon Øigarden" + } + ] + }, + { + ratingKey: "21488", + key: "/library/metadata/21488", + guid: "plex://movie/5d776bd9fb0d55001f574e73", + slug: "the-24-hour-war", + studio: "Chassy Media", + type: "movie", + title: "The 24 Hour War", + titleSort: "24 Hour War", + summary: + "In the early 1960s, Henry Ford II and Enzo Ferrari went to war on the battlefield of Le Mans(TM). This epic battle saw drivers lose their lives, family dynasties nearly collapse and the development of a new race car that changed racing.", + rating: 10.0, + audienceRating: 8.6, + skipCount: 1, + year: 2016, + thumb: "/library/metadata/21488/thumb/1721967875", + art: "/library/metadata/21488/art/1721967875", + duration: 5997792, + originallyAvailableAt: "2016-11-22", + addedAt: 1565072391, + updatedAt: 1721967875, + audienceRatingImage: "rottentomatoes://image.rating.upright", + chapterSource: "media", + ratingImage: "rottentomatoes://image.rating.ripe", + Media: [ + { + id: 21822, + duration: 5997792, + bitrate: 11730, + width: 1920, + height: 1080, + aspectRatio: 1.78, + audioChannels: 6, + audioCodec: "ac3", + videoCodec: "h264", + videoResolution: "1080", + container: "mkv", + videoFrameRate: "24p", + videoProfile: "high", + Part: [ + { + id: 41819, + key: "/library/parts/41819/1492372630/file.mkv", + duration: 5997792, + file: "/Movies/The 24 Hour War (2016)/The.24.Hour.War.2016.1080p.BluRay.DD5.1.x264-HiFi.mkv", + size: 8796183796, + container: "mkv", + indexes: "sd", + videoProfile: "high" + } + ] + } + ], + UltraBlurColors: { + topLeft: "511811", + topRight: "692a1e", + bottomRight: "2d0f0b", + bottomLeft: "551c17" + }, + Genre: [ + { + tag: "Documentary" + }, + { + tag: "History" + } + ], + Country: [ + { + tag: "United States of America" + } + ], + Director: [ + { + tag: "Adam Carolla" + }, + { + tag: "Nate Adams" + } + ], + Role: [ + { + tag: "Mario Andretti" + }, + { + tag: "Bob Bondurant" + }, + { + tag: "Ralph Nader" + } + ] + }, + { + ratingKey: "21050", + key: "/library/metadata/21050", + guid: "plex://movie/5d776824f54112001f5bbdd7", + slug: "28-days-later", + studio: "DNA Films", + type: "movie", + title: "28 Days Later", + originalTitle: "28 Days Later...", + contentRating: "nl/16", + summary: + "Twenty-eight days after a killer virus was accidentally unleashed from a British research facility, a small group of London survivors are caught in a desperate struggle to protect themselves from the infected. Carried by animals and humans, the virus turns those it infects into homicidal maniacs -- and it's absolutely impossible to contain.", + rating: 8.7, + audienceRating: 8.5, + viewOffset: 3421000, + lastViewedAt: 1554844099, + year: 2002, + tagline: + "His fear began when he woke up alone. His terror began when he realised he wasn't.", + thumb: "/library/metadata/21050/thumb/1721967834", + art: "/library/metadata/21050/art/1721967834", + duration: 6787605, + originallyAvailableAt: "2002-11-01", + addedAt: 1563846686, + updatedAt: 1721967834, + audienceRatingImage: "rottentomatoes://image.rating.upright", + ratingImage: "rottentomatoes://image.rating.ripe", + Media: [ + { + id: 21442, + duration: 6787605, + bitrate: 10056, + width: 1920, + height: 1040, + aspectRatio: 1.85, + audioChannels: 6, + audioCodec: "dca", + videoCodec: "h264", + videoResolution: "1080", + container: "mkv", + videoFrameRate: "24p", + audioProfile: "dts", + videoProfile: "high", + Part: [ + { + id: 41881, + key: "/library/parts/41881/1540562456/file.mkv", + duration: 6787605, + file: "/Movies/28 Days Later (2002)/28.Days.Later.2002.1080p.REPACK.BluRay.x264-AVCHD.mkv", + size: 8534411908, + audioProfile: "dts", + container: "mkv", + indexes: "sd", + videoProfile: "high" + } + ] + } + ], + UltraBlurColors: { + topLeft: "571002", + topRight: "6e190f", + bottomRight: "9f1a03", + bottomLeft: "430b04" + }, + Genre: [ + { + tag: "Horror" + }, + { + tag: "Thriller" + } + ], + Country: [ + { + tag: "United Kingdom" + } + ], + Director: [ + { + tag: "Danny Boyle" + } + ], + Writer: [ + { + tag: "Alex Garland" + } + ], + Role: [ + { + tag: "Cillian Murphy" + }, + { + tag: "Naomie Harris" + }, + { + tag: "Brendan Gleeson" + } + ] + } + ] + } + } - validateResponseSpec("/library/sections/{sectionKey}/{tag}", "get", 200, response) - }); + validateResponseSpec( + "/library/sections/{sectionKey}/{tag}", + "get", + 200, + response + ) + }) - it('should validate the 200 response with includeGuids and includeMeta queryParam when the API spec is valid', () => { - // {{baseUrl}}/library/sections/1/all?includeGuids=1&includeMeta=1&X-Plex-Container-Start=1&X-Plex-Container-Size=5 - const response = { - "MediaContainer": { - "size": 5, - "totalSize": 357, - "offset": 1, - "allowSync": true, - "art": "/:/resources/movie-fanart.jpg", - "content": "secondary", - "identifier": "com.plexapp.plugins.library", - "librarySectionID": 1, - "librarySectionTitle": "Movies", - "librarySectionUUID": "0e71027d-88bc-4413-9927-5aad992d3d19", - "mediaTagPrefix": "/system/bundle/media/flags/", - "mediaTagVersion": 1724161316, - "thumb": "/:/resources/movie.png", - "title1": "Movies", - "title2": "All Movies", - "viewGroup": "movie", - "Meta": { - "Type": [{ - "key": "/library/sections/1/all?type=1", - "type": "movie", - "title": "Movies", - "active": true, - "Filter": [{ - "filter": "genre", - "filterType": "string", - "key": "/library/sections/1/genre", - "title": "Genre", - "type": "filter" - }, { - "filter": "year", - "filterType": "integer", - "key": "/library/sections/1/year", - "title": "Year", - "type": "filter" - }, { - "filter": "decade", - "filterType": "integer", - "key": "/library/sections/1/decade", - "title": "Decade", - "type": "filter" - }, { - "filter": "contentRating", - "filterType": "string", - "key": "/library/sections/1/contentRating", - "title": "Content Rating", - "type": "filter" - }, { - "filter": "collection", - "filterType": "string", - "key": "/library/sections/1/collection", - "title": "Collection", - "type": "filter" - }, { - "filter": "director", - "filterType": "string", - "key": "/library/sections/1/director", - "title": "Director", - "type": "filter" - }, { - "filter": "actor", - "filterType": "string", - "key": "/library/sections/1/actor", - "title": "Actor", - "type": "filter" - }, { - "filter": "writer", - "filterType": "string", - "key": "/library/sections/1/writer", - "title": "Writer", - "type": "filter" - }, { - "filter": "producer", - "filterType": "string", - "key": "/library/sections/1/producer", - "title": "Producer", - "type": "filter" - }, { - "filter": "country", - "filterType": "string", - "key": "/library/sections/1/country", - "title": "Country", - "type": "filter" - }, { - "filter": "studio", - "filterType": "string", - "key": "/library/sections/1/studio", - "title": "Studio", - "type": "filter" - }, { - "filter": "resolution", - "filterType": "string", - "key": "/library/sections/1/resolution", - "title": "Resolution", - "type": "filter" - }, { - "filter": "hdr", - "filterType": "boolean", - "key": "/library/sections/1/hdr", - "title": "HDR", - "type": "filter" - }, { - "filter": "unwatched", - "filterType": "boolean", - "key": "/library/sections/1/unwatched", - "title": "Unwatched", - "type": "filter" - }, { - "filter": "inProgress", - "filterType": "boolean", - "key": "/library/sections/1/inProgress", - "title": "In Progress", - "type": "filter" - }, { - "filter": "unmatched", - "filterType": "boolean", - "key": "/library/sections/1/unmatched", - "title": "Unmatched", - "type": "filter" - }, { - "filter": "audioLanguage", - "filterType": "string", - "key": "/library/sections/1/audioLanguage", - "title": "Audio Language", - "type": "filter" - }, { - "filter": "subtitleLanguage", - "filterType": "string", - "key": "/library/sections/1/subtitleLanguage", - "title": "Subtitle Language", - "type": "filter" - }, { - "filter": "label", - "filterType": "string", - "key": "/library/sections/1/label", - "title": "Labels", - "type": "filter" - }], - "Sort": [{ - "active": true, - "activeDirection": "asc", - "default": "asc", - "defaultDirection": "asc", - "descKey": "titleSort:desc", - "firstCharacterKey": "/library/sections/1/firstCharacter", - "key": "titleSort", - "title": "Title" - }, { - "defaultDirection": "desc", - "descKey": "originallyAvailableAt:desc", - "key": "originallyAvailableAt", - "title": "Release Date" - }, { - "defaultDirection": "desc", - "descKey": "rating:desc", - "key": "rating", - "title": "Critic Rating" - }, { - "defaultDirection": "desc", - "descKey": "audienceRating:desc", - "key": "audienceRating", - "title": "Audience Rating" - }, { - "defaultDirection": "desc", - "descKey": "duration:desc", - "key": "duration", - "title": "Duration" - }, { - "defaultDirection": "desc", - "descKey": "addedAt:desc", - "key": "addedAt", - "title": "Date Added" - }, { - "defaultDirection": "desc", - "descKey": "lastViewedAt:desc", - "key": "lastViewedAt", - "title": "Date Viewed" - }, { - "defaultDirection": "asc", - "descKey": "mediaHeight:desc", - "key": "mediaHeight", - "title": "Resolution" - }, { - "defaultDirection": "desc", "descKey": "random:desc", "key": "random", "title": "Randomly" - }], - "Field": [{ - "key": "title", "title": "Title", "type": "string" - }, { - "key": "studio", "title": "Studio", "type": "string" - }, { - "key": "userRating", "subType": "rating", "title": "Rating", "type": "integer" - }, { - "key": "contentRating", "title": "Content Rating", "type": "tag" - }, { - "key": "year", "subType": "year", "title": "Year", "type": "integer" - }, { - "key": "decade", "subType": "decade", "title": "Decade", "type": "integer" - }, { - "key": "originallyAvailableAt", "title": "Release Date", "type": "date" - }, { - "key": "duration", "subType": "duration", "title": "Duration", "type": "integer" - }, { - "key": "unmatched", "title": "Unmatched", "type": "boolean" - }, { - "key": "duplicate", "title": "Duplicate", "type": "boolean" - }, { - "key": "genre", "title": "Genre", "type": "tag" - }, { - "key": "collection", "title": "Collection", "type": "tag" - }, { - "key": "director", "title": "Director", "type": "tag" - }, { - "key": "writer", "title": "Writer", "type": "tag" - }, { - "key": "producer", "title": "Producer", "type": "tag" - }, { - "key": "actor", "title": "Actor", "type": "tag" - }, { - "key": "country", "title": "Country", "type": "tag" - }, { - "key": "addedAt", "title": "Date Added", "type": "date" - }, { - "key": "viewCount", "title": "Plays", "type": "integer" - }, { - "key": "lastViewedAt", "title": "Last Watched", "type": "date" - }, { - "key": "unwatched", "title": "Unwatched", "type": "boolean" - }, { - "key": "resolution", "title": "Resolution", "type": "resolution" - }, { - "key": "hdr", "subType": "hdr", "title": "HDR", "type": "boolean" - }, { - "key": "mediaSize", "subType": "fileSize", "title": "File Size", "type": "integer" - }, { - "key": "mediaBitrate", "subType": "bitrate", "title": "Bitrate", "type": "integer" - }, { - "key": "subtitleLanguage", "title": "Subtitle Language", "type": "subtitleLanguage" - }, { - "key": "audioLanguage", "title": "Audio Language", "type": "audioLanguage" - }, { - "key": "inProgress", "title": "In Progress", "type": "boolean" - }, { - "key": "trash", "title": "Trash", "type": "boolean" - }, { - "key": "label", "title": "Label", "type": "tag" - }] - }, { - "key": "/library/sections/1/folder", "type": "folder", "title": "Folders", "active": false - }], "FieldType": [{ - "type": "tag", "Operator": [{ - "key": "=", "title": "is" - }, { - "key": "!=", "title": "is not" - }] - }, { - "type": "integer", "Operator": [{ - "key": "=", "title": "is" - }, { - "key": "!=", "title": "is not" - }, { - "key": ">>=", "title": "is greater than" - }, { - "key": "<<=", "title": "is less than" - }] - }, { - "type": "string", "Operator": [{ - "key": "=", "title": "contains" - }, { - "key": "!=", "title": "does not contain" - }, { - "key": "==", "title": "is" - }, { - "key": "!==", "title": "is not" - }, { - "key": "<=", "title": "begins with" - }, { - "key": ">=", "title": "ends with" - }] - }, { - "type": "boolean", "Operator": [{ - "key": "=", "title": "is true" - }, { - "key": "!=", "title": "is false" - }] - }, { - "type": "date", "Operator": [{ - "key": "<<=", "title": "is before" - }, { - "key": ">>=", "title": "is after" - }] - }, { - "type": "subtitleLanguage", "Operator": [{ - "key": "=", "title": "is" - }, { - "key": "!=", "title": "is not" - }] - }, { - "type": "audioLanguage", "Operator": [{ - "key": "=", "title": "is" - }, { - "key": "!=", "title": "is not" - }] - }, { - "type": "resolution", "Operator": [{ - "key": "=", "title": "is" - }] - }] + it("should validate the 200 response with includeGuids and includeMeta queryParam when the API spec is valid", () => { + // {{baseUrl}}/library/sections/1/all?includeGuids=1&includeMeta=1&X-Plex-Container-Start=1&X-Plex-Container-Size=5 + const response = { + MediaContainer: { + size: 5, + totalSize: 357, + offset: 1, + allowSync: true, + art: "/:/resources/movie-fanart.jpg", + content: "secondary", + identifier: "com.plexapp.plugins.library", + librarySectionID: 1, + librarySectionTitle: "Movies", + librarySectionUUID: "0e71027d-88bc-4413-9927-5aad992d3d19", + mediaTagPrefix: "/system/bundle/media/flags/", + mediaTagVersion: 1724161316, + thumb: "/:/resources/movie.png", + title1: "Movies", + title2: "All Movies", + viewGroup: "movie", + Meta: { + Type: [ + { + key: "/library/sections/1/all?type=1", + type: "movie", + title: "Movies", + active: true, + Filter: [ + { + filter: "genre", + filterType: "string", + key: "/library/sections/1/genre", + title: "Genre", + type: "filter" }, - "Metadata": [{ - "ratingKey": "9881", - "key": "/library/metadata/9881", - "guid": "plex://movie/5d776834961905001eb939ac", - "slug": "91-2-weeks", - "studio": "Jonesfilm", - "type": "movie", - "title": "9½ Weeks", - "originalTitle": "Nine 1/2 Weeks", - "contentRating": "nl/12", - "summary": "An erotic story about a woman, the assistant of an art gallery, who gets involved in an impersonal affair with a man. She barely knows about his life, only about the sex games they play, so the relationship begins to get complicated.", - "rating": 6.0, - "audienceRating": 5.6, - "year": 1986, - "tagline": "They Broke Every Rule.", - "thumb": "/library/metadata/9881/thumb/1723182007", - "art": "/library/metadata/9881/art/1723182007", - "duration": 7011007, - "originallyAvailableAt": "1986-02-09", - "addedAt": 1560801795, - "updatedAt": 1723182007, - "audienceRatingImage": "rottentomatoes://image.rating.spilled", - "chapterSource": "media", - "ratingImage": "rottentomatoes://image.rating.ripe", - "Media": [{ - "id": 12306, - "duration": 7011007, - "bitrate": 13003, - "width": 1920, - "height": 1080, - "aspectRatio": 1.78, - "audioChannels": 6, - "audioCodec": "dca", - "videoCodec": "h264", - "videoResolution": "1080", - "container": "mkv", - "videoFrameRate": "24p", - "audioProfile": "dts", - "videoProfile": "high", - "Part": [{ - "id": 41930, - "key": "/library/parts/41930/1501053187/file.mkv", - "duration": 7011007, - "file": "/Movies/Nine 1+2 Weeks (1986)/Nine.and.a.Half.Weeks.1986.1080p.BluRay.DTS.x264-HDMaNiAcS.mkv", - "size": 11394952434, - "audioProfile": "dts", - "container": "mkv", - "indexes": "sd", - "videoProfile": "high" - }] - }], - "UltraBlurColors": { - "topLeft": "452410", "topRight": "0d0202", "bottomRight": "0d0202", "bottomLeft": "272223" - }, - "Guid": [{ - "id": "imdb://tt0091635" - }, { - "id": "tmdb://10068" - }, { - "id": "tvdb://1165" - }], - "Genre": [{ - "tag": "Drama" - }, { - "tag": "Romance" - }], - "Country": [{ - "tag": "United States of America" - }], - "Director": [{ - "tag": "Adrian Lyne" - }], - "Writer": [{ - "tag": "Sarah Kernochan" - }, { - "tag": "Zalman King" - }], - "Role": [{ - "tag": "Mickey Rourke" - }, { - "tag": "Kim Basinger" - }, { - "tag": "Margaret Whitton" - }] - }, { - "ratingKey": "20936", - "key": "/library/metadata/20936", - "guid": "plex://movie/5d776831a091de001f2e7756", - "slug": "10000-bc", - "studio": "Centropolis Entertainment", - "type": "movie", - "title": "10,000 BC", - "contentRating": "nl/12", - "summary": "In the prehistoric past, D'Leh is a mammoth hunter who bonds with the beautiful Evolet. When warriors on horseback capture Evolet and the tribesmen, D'Leh must embark on an odyssey to save his true love.", - "rating": 0.9, - "audienceRating": 3.7, - "viewOffset": 3866000, - "lastViewedAt": 1563831721, - "year": 2008, - "tagline": "The legend. The battle. The first hero.", - "thumb": "/library/metadata/20936/thumb/1721967792", - "art": "/library/metadata/20936/art/1721967792", - "duration": 6535680, - "originallyAvailableAt": "2008-03-05", - "addedAt": 1558940057, - "updatedAt": 1721967792, - "audienceRatingImage": "rottentomatoes://image.rating.spilled", - "chapterSource": "media", - "ratingImage": "rottentomatoes://image.rating.rotten", - "Media": [{ - "id": 21335, - "duration": 6535680, - "bitrate": 18740, - "width": 1920, - "height": 1080, - "aspectRatio": 1.78, - "audioChannels": 6, - "audioCodec": "truehd", - "videoCodec": "vc1", - "videoResolution": "1080", - "container": "mkv", - "videoFrameRate": "24p", - "videoProfile": "advanced", - "Part": [{ - "id": 41744, - "key": "/library/parts/41744/1563131036/file.mkv", - "duration": 6535680, - "file": "/Movies/10,000 BC (2008)/10000.BC.2008.1080p.BluRay.REMUX.VC-1.TrueHD.5.1-EPSiLON.mkv", - "size": 15359810466, - "container": "mkv", - "indexes": "sd", - "videoProfile": "advanced" - }] - }], - "UltraBlurColors": { - "topLeft": "173524", "topRight": "8e471e", "bottomRight": "734124", "bottomLeft": "864e20" - }, - "Guid": [{ - "id": "imdb://tt0443649" - }, { - "id": "tmdb://7840" - }, { - "id": "tvdb://1913" - }], - "Genre": [{ - "tag": "Action" - }, { - "tag": "Adventure" - }], - "Country": [{ - "tag": "United States of America" - }, { - "tag": "South Africa" - }], - "Collection": [{ - "tag": "Working NL Subs" - }], - "Director": [{ - "tag": "Roland Emmerich" - }], - "Writer": [{ - "tag": "Harald Kloser" - }, { - "tag": "Roland Emmerich" - }], - "Role": [{ - "tag": "Steven Strait" - }, { - "tag": "Camilla Belle" - }, { - "tag": "Cliff Curtis" - }] - }, { - "ratingKey": "47175", - "key": "/library/metadata/47175", - "guid": "plex://movie/5d776d6c23d5a3001f5230e4", - "slug": "22-july", - "studio": "Scott Rudin Productions", - "type": "movie", - "title": "22 July", - "contentRating": "nl/16", - "summary": "A three-part story of Norway's worst terrorist attack in which over seventy people were killed. 22 July looks at the disaster itself, the survivors, Norway's political system and the lawyers who worked on this horrific case.", - "rating": 8.1, - "audienceRating": 7.0, - "year": 2018, - "tagline": "The true story of a day that started like any other", - "thumb": "/library/metadata/47175/thumb/1721705174", - "art": "/library/metadata/47175/art/1721705174", - "duration": 8640416, - "originallyAvailableAt": "2018-10-04", - "addedAt": 1657821161, - "updatedAt": 1721705174, - "audienceRatingImage": "rottentomatoes://image.rating.upright", - "ratingImage": "rottentomatoes://image.rating.ripe", - "Media": [{ - "id": 50492, - "duration": 8640416, - "bitrate": 15942, - "width": 3840, - "height": 2160, - "aspectRatio": 1.78, - "audioChannels": 6, - "audioCodec": "eac3", - "videoCodec": "hevc", - "videoResolution": "4k", - "container": "mkv", - "videoFrameRate": "24p", - "videoProfile": "main 10", - "Part": [{ - "id": 79867, - "key": "/library/parts/79867/1595149163/file.mkv", - "duration": 8640416, - "file": "/Movies/22 July (2018)/22 July (2018) WEBDL-2160p.mkv", - "size": 17221974810, - "container": "mkv", - "indexes": "sd", - "videoProfile": "main 10" - }] - }], - "UltraBlurColors": { - "topLeft": "243032", "topRight": "245968", "bottomRight": "113140", "bottomLeft": "1b1d1f" - }, - "Guid": [{ - "id": "imdb://tt7280898" - }, { - "id": "tmdb://474354" - }, { - "id": "tvdb://968" - }], - "Genre": [{ - "tag": "Drama" - }, { - "tag": "History" - }], - "Country": [{ - "tag": "Iceland" - }, { - "tag": "Norway" - }], - "Director": [{ - "tag": "Paul Greengrass" - }], - "Writer": [{ - "tag": "Åsne Seierstad" - }, { - "tag": "Paul Greengrass" - }], - "Role": [{ - "tag": "Jonas Strand Gravli" - }, { - "tag": "Anders Danielsen Lie" - }, { - "tag": "Jon Øigarden" - }] - }, { - "ratingKey": "21488", - "key": "/library/metadata/21488", - "guid": "plex://movie/5d776bd9fb0d55001f574e73", - "slug": "the-24-hour-war", - "studio": "Chassy Media", - "type": "movie", - "title": "The 24 Hour War", - "titleSort": "24 Hour War", - "summary": "In the early 1960s, Henry Ford II and Enzo Ferrari went to war on the battlefield of Le Mans(TM). This epic battle saw drivers lose their lives, family dynasties nearly collapse and the development of a new race car that changed racing.", - "rating": 10.0, - "audienceRating": 8.6, - "skipCount": 1, - "year": 2016, - "thumb": "/library/metadata/21488/thumb/1721967875", - "art": "/library/metadata/21488/art/1721967875", - "duration": 5997792, - "originallyAvailableAt": "2016-11-22", - "addedAt": 1565072391, - "updatedAt": 1721967875, - "audienceRatingImage": "rottentomatoes://image.rating.upright", - "chapterSource": "media", - "ratingImage": "rottentomatoes://image.rating.ripe", - "Media": [{ - "id": 21822, - "duration": 5997792, - "bitrate": 11730, - "width": 1920, - "height": 1080, - "aspectRatio": 1.78, - "audioChannels": 6, - "audioCodec": "ac3", - "videoCodec": "h264", - "videoResolution": "1080", - "container": "mkv", - "videoFrameRate": "24p", - "videoProfile": "high", - "Part": [{ - "id": 41819, - "key": "/library/parts/41819/1492372630/file.mkv", - "duration": 5997792, - "file": "/Movies/The 24 Hour War (2016)/The.24.Hour.War.2016.1080p.BluRay.DD5.1.x264-HiFi.mkv", - "size": 8796183796, - "container": "mkv", - "indexes": "sd", - "videoProfile": "high" - }] - }], - "UltraBlurColors": { - "topLeft": "511811", "topRight": "692a1e", "bottomRight": "2d0f0b", "bottomLeft": "551c17" - }, - "Guid": [{ - "id": "imdb://tt4875844" - }, { - "id": "tmdb://359093" - }, { - "id": "tvdb://132451" - }], - "Genre": [{ - "tag": "Documentary" - }, { - "tag": "History" - }], - "Country": [{ - "tag": "United States of America" - }], - "Director": [{ - "tag": "Adam Carolla" - }, { - "tag": "Nate Adams" - }], - "Role": [{ - "tag": "Mario Andretti" - }, { - "tag": "Bob Bondurant" - }, { - "tag": "Ralph Nader" - }] - }, { - "ratingKey": "21050", - "key": "/library/metadata/21050", - "guid": "plex://movie/5d776824f54112001f5bbdd7", - "slug": "28-days-later", - "studio": "DNA Films", - "type": "movie", - "title": "28 Days Later", - "originalTitle": "28 Days Later...", - "contentRating": "nl/16", - "summary": "Twenty-eight days after a killer virus was accidentally unleashed from a British research facility, a small group of London survivors are caught in a desperate struggle to protect themselves from the infected. Carried by animals and humans, the virus turns those it infects into homicidal maniacs -- and it's absolutely impossible to contain.", - "rating": 8.7, - "audienceRating": 8.5, - "viewOffset": 3421000, - "lastViewedAt": 1554844099, - "year": 2002, - "tagline": "His fear began when he woke up alone. His terror began when he realised he wasn't.", - "thumb": "/library/metadata/21050/thumb/1721967834", - "art": "/library/metadata/21050/art/1721967834", - "duration": 6787605, - "originallyAvailableAt": "2002-11-01", - "addedAt": 1563846686, - "updatedAt": 1721967834, - "audienceRatingImage": "rottentomatoes://image.rating.upright", - "ratingImage": "rottentomatoes://image.rating.ripe", - "Media": [{ - "id": 21442, - "duration": 6787605, - "bitrate": 10056, - "width": 1920, - "height": 1040, - "aspectRatio": 1.85, - "audioChannels": 6, - "audioCodec": "dca", - "videoCodec": "h264", - "videoResolution": "1080", - "container": "mkv", - "videoFrameRate": "24p", - "audioProfile": "dts", - "videoProfile": "high", - "Part": [{ - "id": 41881, - "key": "/library/parts/41881/1540562456/file.mkv", - "duration": 6787605, - "file": "/Movies/28 Days Later (2002)/28.Days.Later.2002.1080p.REPACK.BluRay.x264-AVCHD.mkv", - "size": 8534411908, - "audioProfile": "dts", - "container": "mkv", - "indexes": "sd", - "videoProfile": "high" - }] - }], - "UltraBlurColors": { - "topLeft": "571002", "topRight": "6e190f", "bottomRight": "9f1a03", "bottomLeft": "430b04" - }, - "Guid": [{ - "id": "imdb://tt0289043" - }, { - "id": "tmdb://170" - }, { - "id": "tvdb://871" - }], - "Genre": [{ - "tag": "Horror" - }, { - "tag": "Thriller" - }], - "Country": [{ - "tag": "United Kingdom" - }], - "Director": [{ - "tag": "Danny Boyle" - }], - "Writer": [{ - "tag": "Alex Garland" - }], - "Role": [{ - "tag": "Cillian Murphy" - }, { - "tag": "Naomie Harris" - }, { - "tag": "Brendan Gleeson" - }] - }] + { + filter: "year", + filterType: "integer", + key: "/library/sections/1/year", + title: "Year", + type: "filter" + }, + { + filter: "decade", + filterType: "integer", + key: "/library/sections/1/decade", + title: "Decade", + type: "filter" + }, + { + filter: "contentRating", + filterType: "string", + key: "/library/sections/1/contentRating", + title: "Content Rating", + type: "filter" + }, + { + filter: "collection", + filterType: "string", + key: "/library/sections/1/collection", + title: "Collection", + type: "filter" + }, + { + filter: "director", + filterType: "string", + key: "/library/sections/1/director", + title: "Director", + type: "filter" + }, + { + filter: "actor", + filterType: "string", + key: "/library/sections/1/actor", + title: "Actor", + type: "filter" + }, + { + filter: "writer", + filterType: "string", + key: "/library/sections/1/writer", + title: "Writer", + type: "filter" + }, + { + filter: "producer", + filterType: "string", + key: "/library/sections/1/producer", + title: "Producer", + type: "filter" + }, + { + filter: "country", + filterType: "string", + key: "/library/sections/1/country", + title: "Country", + type: "filter" + }, + { + filter: "studio", + filterType: "string", + key: "/library/sections/1/studio", + title: "Studio", + type: "filter" + }, + { + filter: "resolution", + filterType: "string", + key: "/library/sections/1/resolution", + title: "Resolution", + type: "filter" + }, + { + filter: "hdr", + filterType: "boolean", + key: "/library/sections/1/hdr", + title: "HDR", + type: "filter" + }, + { + filter: "unwatched", + filterType: "boolean", + key: "/library/sections/1/unwatched", + title: "Unwatched", + type: "filter" + }, + { + filter: "inProgress", + filterType: "boolean", + key: "/library/sections/1/inProgress", + title: "In Progress", + type: "filter" + }, + { + filter: "unmatched", + filterType: "boolean", + key: "/library/sections/1/unmatched", + title: "Unmatched", + type: "filter" + }, + { + filter: "audioLanguage", + filterType: "string", + key: "/library/sections/1/audioLanguage", + title: "Audio Language", + type: "filter" + }, + { + filter: "subtitleLanguage", + filterType: "string", + key: "/library/sections/1/subtitleLanguage", + title: "Subtitle Language", + type: "filter" + }, + { + filter: "label", + filterType: "string", + key: "/library/sections/1/label", + title: "Labels", + type: "filter" + } + ], + Sort: [ + { + active: true, + activeDirection: "asc", + default: "asc", + defaultDirection: "asc", + descKey: "titleSort:desc", + firstCharacterKey: "/library/sections/1/firstCharacter", + key: "titleSort", + title: "Title" + }, + { + defaultDirection: "desc", + descKey: "originallyAvailableAt:desc", + key: "originallyAvailableAt", + title: "Release Date" + }, + { + defaultDirection: "desc", + descKey: "rating:desc", + key: "rating", + title: "Critic Rating" + }, + { + defaultDirection: "desc", + descKey: "audienceRating:desc", + key: "audienceRating", + title: "Audience Rating" + }, + { + defaultDirection: "desc", + descKey: "duration:desc", + key: "duration", + title: "Duration" + }, + { + defaultDirection: "desc", + descKey: "addedAt:desc", + key: "addedAt", + title: "Date Added" + }, + { + defaultDirection: "desc", + descKey: "lastViewedAt:desc", + key: "lastViewedAt", + title: "Date Viewed" + }, + { + defaultDirection: "asc", + descKey: "mediaHeight:desc", + key: "mediaHeight", + title: "Resolution" + }, + { + defaultDirection: "desc", + descKey: "random:desc", + key: "random", + title: "Randomly" + } + ], + Field: [ + { + key: "title", + title: "Title", + type: "string" + }, + { + key: "studio", + title: "Studio", + type: "string" + }, + { + key: "userRating", + subType: "rating", + title: "Rating", + type: "integer" + }, + { + key: "contentRating", + title: "Content Rating", + type: "tag" + }, + { + key: "year", + subType: "year", + title: "Year", + type: "integer" + }, + { + key: "decade", + subType: "decade", + title: "Decade", + type: "integer" + }, + { + key: "originallyAvailableAt", + title: "Release Date", + type: "date" + }, + { + key: "duration", + subType: "duration", + title: "Duration", + type: "integer" + }, + { + key: "unmatched", + title: "Unmatched", + type: "boolean" + }, + { + key: "duplicate", + title: "Duplicate", + type: "boolean" + }, + { + key: "genre", + title: "Genre", + type: "tag" + }, + { + key: "collection", + title: "Collection", + type: "tag" + }, + { + key: "director", + title: "Director", + type: "tag" + }, + { + key: "writer", + title: "Writer", + type: "tag" + }, + { + key: "producer", + title: "Producer", + type: "tag" + }, + { + key: "actor", + title: "Actor", + type: "tag" + }, + { + key: "country", + title: "Country", + type: "tag" + }, + { + key: "addedAt", + title: "Date Added", + type: "date" + }, + { + key: "viewCount", + title: "Plays", + type: "integer" + }, + { + key: "lastViewedAt", + title: "Last Watched", + type: "date" + }, + { + key: "unwatched", + title: "Unwatched", + type: "boolean" + }, + { + key: "resolution", + title: "Resolution", + type: "resolution" + }, + { + key: "hdr", + subType: "hdr", + title: "HDR", + type: "boolean" + }, + { + key: "mediaSize", + subType: "fileSize", + title: "File Size", + type: "integer" + }, + { + key: "mediaBitrate", + subType: "bitrate", + title: "Bitrate", + type: "integer" + }, + { + key: "subtitleLanguage", + title: "Subtitle Language", + type: "subtitleLanguage" + }, + { + key: "audioLanguage", + title: "Audio Language", + type: "audioLanguage" + }, + { + key: "inProgress", + title: "In Progress", + type: "boolean" + }, + { + key: "trash", + title: "Trash", + type: "boolean" + }, + { + key: "label", + title: "Label", + type: "tag" + } + ] + }, + { + key: "/library/sections/1/folder", + type: "folder", + title: "Folders", + active: false } - }; - - validateResponseSpec("/library/sections/{sectionKey}/{tag}", "get", 200, response) - }); + ], + FieldType: [ + { + type: "tag", + Operator: [ + { + key: "=", + title: "is" + }, + { + key: "!=", + title: "is not" + } + ] + }, + { + type: "integer", + Operator: [ + { + key: "=", + title: "is" + }, + { + key: "!=", + title: "is not" + }, + { + key: ">>=", + title: "is greater than" + }, + { + key: "<<=", + title: "is less than" + } + ] + }, + { + type: "string", + Operator: [ + { + key: "=", + title: "contains" + }, + { + key: "!=", + title: "does not contain" + }, + { + key: "==", + title: "is" + }, + { + key: "!==", + title: "is not" + }, + { + key: "<=", + title: "begins with" + }, + { + key: ">=", + title: "ends with" + } + ] + }, + { + type: "boolean", + Operator: [ + { + key: "=", + title: "is true" + }, + { + key: "!=", + title: "is false" + } + ] + }, + { + type: "date", + Operator: [ + { + key: "<<=", + title: "is before" + }, + { + key: ">>=", + title: "is after" + } + ] + }, + { + type: "subtitleLanguage", + Operator: [ + { + key: "=", + title: "is" + }, + { + key: "!=", + title: "is not" + } + ] + }, + { + type: "audioLanguage", + Operator: [ + { + key: "=", + title: "is" + }, + { + key: "!=", + title: "is not" + } + ] + }, + { + type: "resolution", + Operator: [ + { + key: "=", + title: "is" + } + ] + } + ] + }, + Metadata: [ + { + ratingKey: "9881", + key: "/library/metadata/9881", + guid: "plex://movie/5d776834961905001eb939ac", + slug: "91-2-weeks", + studio: "Jonesfilm", + type: "movie", + title: "9½ Weeks", + originalTitle: "Nine 1/2 Weeks", + contentRating: "nl/12", + summary: + "An erotic story about a woman, the assistant of an art gallery, who gets involved in an impersonal affair with a man. She barely knows about his life, only about the sex games they play, so the relationship begins to get complicated.", + rating: 6.0, + audienceRating: 5.6, + year: 1986, + tagline: "They Broke Every Rule.", + thumb: "/library/metadata/9881/thumb/1723182007", + art: "/library/metadata/9881/art/1723182007", + duration: 7011007, + originallyAvailableAt: "1986-02-09", + addedAt: 1560801795, + updatedAt: 1723182007, + audienceRatingImage: "rottentomatoes://image.rating.spilled", + chapterSource: "media", + ratingImage: "rottentomatoes://image.rating.ripe", + Media: [ + { + id: 12306, + duration: 7011007, + bitrate: 13003, + width: 1920, + height: 1080, + aspectRatio: 1.78, + audioChannels: 6, + audioCodec: "dca", + videoCodec: "h264", + videoResolution: "1080", + container: "mkv", + videoFrameRate: "24p", + audioProfile: "dts", + videoProfile: "high", + Part: [ + { + id: 41930, + key: "/library/parts/41930/1501053187/file.mkv", + duration: 7011007, + file: "/Movies/Nine 1+2 Weeks (1986)/Nine.and.a.Half.Weeks.1986.1080p.BluRay.DTS.x264-HDMaNiAcS.mkv", + size: 11394952434, + audioProfile: "dts", + container: "mkv", + indexes: "sd", + videoProfile: "high" + } + ] + } + ], + UltraBlurColors: { + topLeft: "452410", + topRight: "0d0202", + bottomRight: "0d0202", + bottomLeft: "272223" + }, + Guid: [ + { + id: "imdb://tt0091635" + }, + { + id: "tmdb://10068" + }, + { + id: "tvdb://1165" + } + ], + Genre: [ + { + tag: "Drama" + }, + { + tag: "Romance" + } + ], + Country: [ + { + tag: "United States of America" + } + ], + Director: [ + { + tag: "Adrian Lyne" + } + ], + Writer: [ + { + tag: "Sarah Kernochan" + }, + { + tag: "Zalman King" + } + ], + Role: [ + { + tag: "Mickey Rourke" + }, + { + tag: "Kim Basinger" + }, + { + tag: "Margaret Whitton" + } + ] + }, + { + ratingKey: "20936", + key: "/library/metadata/20936", + guid: "plex://movie/5d776831a091de001f2e7756", + slug: "10000-bc", + studio: "Centropolis Entertainment", + type: "movie", + title: "10,000 BC", + contentRating: "nl/12", + summary: + "In the prehistoric past, D'Leh is a mammoth hunter who bonds with the beautiful Evolet. When warriors on horseback capture Evolet and the tribesmen, D'Leh must embark on an odyssey to save his true love.", + rating: 0.9, + audienceRating: 3.7, + viewOffset: 3866000, + lastViewedAt: 1563831721, + year: 2008, + tagline: "The legend. The battle. The first hero.", + thumb: "/library/metadata/20936/thumb/1721967792", + art: "/library/metadata/20936/art/1721967792", + duration: 6535680, + originallyAvailableAt: "2008-03-05", + addedAt: 1558940057, + updatedAt: 1721967792, + audienceRatingImage: "rottentomatoes://image.rating.spilled", + chapterSource: "media", + ratingImage: "rottentomatoes://image.rating.rotten", + Media: [ + { + id: 21335, + duration: 6535680, + bitrate: 18740, + width: 1920, + height: 1080, + aspectRatio: 1.78, + audioChannels: 6, + audioCodec: "truehd", + videoCodec: "vc1", + videoResolution: "1080", + container: "mkv", + videoFrameRate: "24p", + videoProfile: "advanced", + Part: [ + { + id: 41744, + key: "/library/parts/41744/1563131036/file.mkv", + duration: 6535680, + file: "/Movies/10,000 BC (2008)/10000.BC.2008.1080p.BluRay.REMUX.VC-1.TrueHD.5.1-EPSiLON.mkv", + size: 15359810466, + container: "mkv", + indexes: "sd", + videoProfile: "advanced" + } + ] + } + ], + UltraBlurColors: { + topLeft: "173524", + topRight: "8e471e", + bottomRight: "734124", + bottomLeft: "864e20" + }, + Guid: [ + { + id: "imdb://tt0443649" + }, + { + id: "tmdb://7840" + }, + { + id: "tvdb://1913" + } + ], + Genre: [ + { + tag: "Action" + }, + { + tag: "Adventure" + } + ], + Country: [ + { + tag: "United States of America" + }, + { + tag: "South Africa" + } + ], + Collection: [ + { + tag: "Working NL Subs" + } + ], + Director: [ + { + tag: "Roland Emmerich" + } + ], + Writer: [ + { + tag: "Harald Kloser" + }, + { + tag: "Roland Emmerich" + } + ], + Role: [ + { + tag: "Steven Strait" + }, + { + tag: "Camilla Belle" + }, + { + tag: "Cliff Curtis" + } + ] + }, + { + ratingKey: "47175", + key: "/library/metadata/47175", + guid: "plex://movie/5d776d6c23d5a3001f5230e4", + slug: "22-july", + studio: "Scott Rudin Productions", + type: "movie", + title: "22 July", + contentRating: "nl/16", + summary: + "A three-part story of Norway's worst terrorist attack in which over seventy people were killed. 22 July looks at the disaster itself, the survivors, Norway's political system and the lawyers who worked on this horrific case.", + rating: 8.1, + audienceRating: 7.0, + year: 2018, + tagline: "The true story of a day that started like any other", + thumb: "/library/metadata/47175/thumb/1721705174", + art: "/library/metadata/47175/art/1721705174", + duration: 8640416, + originallyAvailableAt: "2018-10-04", + addedAt: 1657821161, + updatedAt: 1721705174, + audienceRatingImage: "rottentomatoes://image.rating.upright", + ratingImage: "rottentomatoes://image.rating.ripe", + Media: [ + { + id: 50492, + duration: 8640416, + bitrate: 15942, + width: 3840, + height: 2160, + aspectRatio: 1.78, + audioChannels: 6, + audioCodec: "eac3", + videoCodec: "hevc", + videoResolution: "4k", + container: "mkv", + videoFrameRate: "24p", + videoProfile: "main 10", + Part: [ + { + id: 79867, + key: "/library/parts/79867/1595149163/file.mkv", + duration: 8640416, + file: "/Movies/22 July (2018)/22 July (2018) WEBDL-2160p.mkv", + size: 17221974810, + container: "mkv", + indexes: "sd", + videoProfile: "main 10" + } + ] + } + ], + UltraBlurColors: { + topLeft: "243032", + topRight: "245968", + bottomRight: "113140", + bottomLeft: "1b1d1f" + }, + Guid: [ + { + id: "imdb://tt7280898" + }, + { + id: "tmdb://474354" + }, + { + id: "tvdb://968" + } + ], + Genre: [ + { + tag: "Drama" + }, + { + tag: "History" + } + ], + Country: [ + { + tag: "Iceland" + }, + { + tag: "Norway" + } + ], + Director: [ + { + tag: "Paul Greengrass" + } + ], + Writer: [ + { + tag: "Åsne Seierstad" + }, + { + tag: "Paul Greengrass" + } + ], + Role: [ + { + tag: "Jonas Strand Gravli" + }, + { + tag: "Anders Danielsen Lie" + }, + { + tag: "Jon Øigarden" + } + ] + }, + { + ratingKey: "21488", + key: "/library/metadata/21488", + guid: "plex://movie/5d776bd9fb0d55001f574e73", + slug: "the-24-hour-war", + studio: "Chassy Media", + type: "movie", + title: "The 24 Hour War", + titleSort: "24 Hour War", + summary: + "In the early 1960s, Henry Ford II and Enzo Ferrari went to war on the battlefield of Le Mans(TM). This epic battle saw drivers lose their lives, family dynasties nearly collapse and the development of a new race car that changed racing.", + rating: 10.0, + audienceRating: 8.6, + skipCount: 1, + year: 2016, + thumb: "/library/metadata/21488/thumb/1721967875", + art: "/library/metadata/21488/art/1721967875", + duration: 5997792, + originallyAvailableAt: "2016-11-22", + addedAt: 1565072391, + updatedAt: 1721967875, + audienceRatingImage: "rottentomatoes://image.rating.upright", + chapterSource: "media", + ratingImage: "rottentomatoes://image.rating.ripe", + Media: [ + { + id: 21822, + duration: 5997792, + bitrate: 11730, + width: 1920, + height: 1080, + aspectRatio: 1.78, + audioChannels: 6, + audioCodec: "ac3", + videoCodec: "h264", + videoResolution: "1080", + container: "mkv", + videoFrameRate: "24p", + videoProfile: "high", + Part: [ + { + id: 41819, + key: "/library/parts/41819/1492372630/file.mkv", + duration: 5997792, + file: "/Movies/The 24 Hour War (2016)/The.24.Hour.War.2016.1080p.BluRay.DD5.1.x264-HiFi.mkv", + size: 8796183796, + container: "mkv", + indexes: "sd", + videoProfile: "high" + } + ] + } + ], + UltraBlurColors: { + topLeft: "511811", + topRight: "692a1e", + bottomRight: "2d0f0b", + bottomLeft: "551c17" + }, + Guid: [ + { + id: "imdb://tt4875844" + }, + { + id: "tmdb://359093" + }, + { + id: "tvdb://132451" + } + ], + Genre: [ + { + tag: "Documentary" + }, + { + tag: "History" + } + ], + Country: [ + { + tag: "United States of America" + } + ], + Director: [ + { + tag: "Adam Carolla" + }, + { + tag: "Nate Adams" + } + ], + Role: [ + { + tag: "Mario Andretti" + }, + { + tag: "Bob Bondurant" + }, + { + tag: "Ralph Nader" + } + ] + }, + { + ratingKey: "21050", + key: "/library/metadata/21050", + guid: "plex://movie/5d776824f54112001f5bbdd7", + slug: "28-days-later", + studio: "DNA Films", + type: "movie", + title: "28 Days Later", + originalTitle: "28 Days Later...", + contentRating: "nl/16", + summary: + "Twenty-eight days after a killer virus was accidentally unleashed from a British research facility, a small group of London survivors are caught in a desperate struggle to protect themselves from the infected. Carried by animals and humans, the virus turns those it infects into homicidal maniacs -- and it's absolutely impossible to contain.", + rating: 8.7, + audienceRating: 8.5, + viewOffset: 3421000, + lastViewedAt: 1554844099, + year: 2002, + tagline: + "His fear began when he woke up alone. His terror began when he realised he wasn't.", + thumb: "/library/metadata/21050/thumb/1721967834", + art: "/library/metadata/21050/art/1721967834", + duration: 6787605, + originallyAvailableAt: "2002-11-01", + addedAt: 1563846686, + updatedAt: 1721967834, + audienceRatingImage: "rottentomatoes://image.rating.upright", + ratingImage: "rottentomatoes://image.rating.ripe", + Media: [ + { + id: 21442, + duration: 6787605, + bitrate: 10056, + width: 1920, + height: 1040, + aspectRatio: 1.85, + audioChannels: 6, + audioCodec: "dca", + videoCodec: "h264", + videoResolution: "1080", + container: "mkv", + videoFrameRate: "24p", + audioProfile: "dts", + videoProfile: "high", + Part: [ + { + id: 41881, + key: "/library/parts/41881/1540562456/file.mkv", + duration: 6787605, + file: "/Movies/28 Days Later (2002)/28.Days.Later.2002.1080p.REPACK.BluRay.x264-AVCHD.mkv", + size: 8534411908, + audioProfile: "dts", + container: "mkv", + indexes: "sd", + videoProfile: "high" + } + ] + } + ], + UltraBlurColors: { + topLeft: "571002", + topRight: "6e190f", + bottomRight: "9f1a03", + bottomLeft: "430b04" + }, + Guid: [ + { + id: "imdb://tt0289043" + }, + { + id: "tmdb://170" + }, + { + id: "tvdb://871" + } + ], + Genre: [ + { + tag: "Horror" + }, + { + tag: "Thriller" + } + ], + Country: [ + { + tag: "United Kingdom" + } + ], + Director: [ + { + tag: "Danny Boyle" + } + ], + Writer: [ + { + tag: "Alex Garland" + } + ], + Role: [ + { + tag: "Cillian Murphy" + }, + { + tag: "Naomie Harris" + }, + { + tag: "Brendan Gleeson" + } + ] + } + ] + } + } + validateResponseSpec( + "/library/sections/{sectionKey}/{tag}", + "get", + 200, + response + ) + }) }) diff --git a/tests/paths/library/get-all-libraries.spec.ts b/tests/paths/library/get-all-libraries.spec.ts index 343babdd..e4ada410 100644 --- a/tests/paths/library/get-all-libraries.spec.ts +++ b/tests/paths/library/get-all-libraries.spec.ts @@ -1,414 +1,414 @@ -import {validateResponseSpec} from "../../utils/"; -import {describe, it} from 'vitest' +import { validateResponseSpec } from "../../utils/" +import { describe, it } from "vitest" -describe('GET /library/sections', () => { - it('should validate the 200 response when the API spec is valid', () => { - const response = { - "MediaContainer": { - "size": 14, - "allowSync": false, - "title1": "Plex Library", - "Directory": [ - { - "allowSync": true, - "art": "/:/resources/movie-fanart.jpg", - "composite": "/library/sections/7/composite/1893047123", - "filters": true, - "refreshing": false, - "thumb": "/:/resources/movie.png", - "key": "7", - "type": "movie", - "title": "Kids Movies", - "agent": "tv.plex.agents.movie", - "scanner": "Plex Movie", - "language": "en-US", - "uuid": "a1b2c3d4e5f67890", - "updatedAt": 1728394001, - "createdAt": 1598476504, - "scannedAt": 1893047123, - "content": true, - "directory": true, - "contentChangedAt": 4738921, - "hidden": 0, - "Location": [ - { - "id": 25, - "path": "/KidsMovies" - } - ] - }, - { - "allowSync": true, - "art": "/:/resources/movie-fanart.jpg", - "composite": "/library/sections/13/composite/1893047124", - "filters": true, - "refreshing": false, - "thumb": "/:/resources/movie.png", - "key": "13", - "type": "movie", - "title": "Kids Movies NL", - "agent": "tv.plex.agents.movie", - "scanner": "Plex Movie", - "language": "nl-NL", - "uuid": "b2c3d4e5f67890a1", - "updatedAt": 1680007500, - "createdAt": 1680007500, - "scannedAt": 1893047124, - "content": true, - "directory": true, - "contentChangedAt": 5283714, - "hidden": 0, - "Location": [ - { - "id": 23, - "path": "/KidsMoviesNL" - } - ] - }, - { - "allowSync": true, - "art": "/:/resources/movie-fanart.jpg", - "composite": "/library/sections/1/composite/1893047130", - "filters": true, - "refreshing": false, - "thumb": "/:/resources/movie.png", - "key": "1", - "type": "movie", - "title": "Movies", - "agent": "tv.plex.agents.movie", - "scanner": "Plex Movie", - "language": "en-US", - "uuid": "c3d4e5f67890a1b2", - "updatedAt": 1728394005, - "createdAt": 1598476200, - "scannedAt": 1893047130, - "content": true, - "directory": true, - "contentChangedAt": 6379184, - "hidden": 0, - "Location": [ - { - "id": 18, - "path": "/Movies" - } - ] - }, - { - "allowSync": true, - "art": "/:/resources/movie-fanart.jpg", - "composite": "/library/sections/6/composite/1893047135", - "filters": true, - "refreshing": false, - "thumb": "/:/resources/movie.png", - "key": "6", - "type": "movie", - "title": "Movies (Documentaries)", - "agent": "tv.plex.agents.movie", - "scanner": "Plex Movie", - "language": "en-US", - "uuid": "d4e5f67890a1b2c3", - "updatedAt": 1728394010, - "createdAt": 1598476302, - "scannedAt": 1893047135, - "content": true, - "directory": true, - "contentChangedAt": 5293874, - "hidden": 0, - "Location": [ - { - "id": 29, - "path": "/Movies (Documentaries)" - }, - { - "id": 15, - "path": "/Plex Library/Documentaries" - } - ] - }, - { - "allowSync": true, - "art": "/:/resources/movie-fanart.jpg", - "composite": "/library/sections/15/composite/1893047145", - "filters": true, - "refreshing": false, - "thumb": "/:/resources/movie.png", - "key": "15", - "type": "movie", - "title": "Test Media Movies", - "agent": "tv.plex.agents.movie", - "scanner": "Plex Movie", - "language": "en-US", - "uuid": "e5f67890a1b2c3d4", - "updatedAt": 1689075000, - "createdAt": 1689075000, - "scannedAt": 1893047145, - "content": true, - "directory": true, - "contentChangedAt": 5182738, - "hidden": 0, - "Location": [ - { - "id": 27, - "path": "/TestMedia/Movies" - } - ] - }, - { - "allowSync": true, - "art": "/:/resources/show-fanart.jpg", - "composite": "/library/sections/3/composite/1893047110", - "filters": true, - "refreshing": false, - "thumb": "/:/resources/show.png", - "key": "3", - "type": "show", - "title": "Anime", - "agent": "com.plexapp.agents.hama", - "scanner": "Plex Series Scanner", - "language": "en", - "uuid": "f67890a1b2c3d4e5", - "updatedAt": 1684970001, - "createdAt": 1598476000, - "scannedAt": 1893047110, - "content": true, - "directory": true, - "contentChangedAt": 8379201, - "hidden": 0, - "Location": [ - { - "id": 17, - "path": "/Anime" - } - ] - }, - { - "allowSync": true, - "art": "/:/resources/show-fanart.jpg", - "composite": "/library/sections/12/composite/1893047125", - "filters": true, - "refreshing": false, - "thumb": "/:/resources/show.png", - "key": "12", - "type": "show", - "title": "Kids Tv Shows NL", - "agent": "tv.plex.agents.series", - "scanner": "Plex TV Series", - "language": "nl-NL", - "uuid": "67890a1b2c3d4e5f", - "updatedAt": 1728394002, - "createdAt": 1680007400, - "scannedAt": 1893047125, - "content": true, - "directory": true, - "contentChangedAt": 5948203, - "hidden": 0, - "Location": [ - { - "id": 22, - "path": "/KidsTvShowsNL" - } - ] - }, - { - "allowSync": true, - "art": "/:/resources/show-fanart.jpg", - "composite": "/library/sections/14/composite/1893047145", - "filters": true, - "refreshing": false, - "thumb": "/:/resources/show.png", - "key": "14", - "type": "show", - "title": "Reality TV (NL)", - "agent": "tv.plex.agents.series", - "scanner": "Plex TV Series", - "language": "nl-NL", - "uuid": "890a1b2c3d4e5f67", - "updatedAt": 1728394007, - "createdAt": 1601860600, - "scannedAt": 1893047145, - "content": true, - "directory": true, - "contentChangedAt": 6283720, - "hidden": 0, - "Location": [ - { - "id": 26, - "path": "/Reality TV NL" - } - ] - }, - { - "allowSync": true, - "art": "/:/resources/show-fanart.jpg", - "composite": "/library/sections/2/composite/1893047150", - "filters": true, - "refreshing": false, - "thumb": "/:/resources/show.png", - "key": "2", - "type": "show", - "title": "TV Series ", - "agent": "tv.plex.agents.series", - "scanner": "Plex TV Series", - "language": "en-US", - "uuid": "a1b2c3d4e5f67890", - "updatedAt": 1728394003, - "createdAt": 1598476100, - "scannedAt": 1893047150, - "content": true, - "directory": true, - "contentChangedAt": 6472184, - "hidden": 0, - "Location": [ - { - "id": 32, - "path": "/TV Shows" - } - ] - }, - { - "allowSync": true, - "art": "/:/resources/show-fanart.jpg", - "composite": "/library/sections/16/composite/1893047155", - "filters": true, - "refreshing": false, - "thumb": "/:/resources/show.png", - "key": "16", - "type": "show", - "title": "TV Shows (Documentaries)", - "agent": "tv.plex.agents.series", - "scanner": "Plex TV Series", - "language": "en-US", - "uuid": "b2c3d4e5f67890a1", - "updatedAt": 1689076000, - "createdAt": 1689076000, - "scannedAt": 1893047155, - "content": true, - "directory": true, - "contentChangedAt": 4920835, - "hidden": 0, - "Location": [ - { - "id": 28, - "path": "/TV Shows (Documentaries)" - } - ] - }, - { - "allowSync": true, - "art": "/:/resources/show-fanart.jpg", - "composite": "/library/sections/17/composite/1893047155", - "filters": true, - "refreshing": false, - "thumb": "/:/resources/show.png", - "key": "17", - "type": "show", - "title": "TV Shows (Kids)", - "agent": "tv.plex.agents.series", - "scanner": "Plex TV Series", - "language": "en-US", - "uuid": "c3d4e5f67890a1b2", - "updatedAt": 1689077000, - "createdAt": 1689077000, - "scannedAt": 1893047155, - "content": true, - "directory": true, - "contentChangedAt": 5309283, - "hidden": 0, - "Location": [ - { - "id": 31, - "path": "/TV Shows (Kids)" - } - ] - }, - { - "allowSync": true, - "art": "/:/resources/show-fanart.jpg", - "composite": "/library/sections/10/composite/1893047170", - "filters": true, - "refreshing": false, - "thumb": "/:/resources/show.png", - "key": "10", - "type": "show", - "title": "TV Shows (Reality)", - "agent": "tv.plex.agents.series", - "scanner": "Plex TV Series", - "language": "en-US", - "uuid": "d4e5f67890a1b2c3", - "updatedAt": 1689078000, - "createdAt": 1626704821, - "scannedAt": 1893047170, - "content": true, - "directory": true, - "contentChangedAt": 7291885, - "hidden": 0, - "Location": [ - { - "id": 30, - "path": "/TV Shows (Reality)" - } - ] - }, - { - "allowSync": true, - "art": "/:/resources/artist-fanart.jpg", - "composite": "/library/sections/9/composite/1893047140", - "filters": true, - "refreshing": false, - "thumb": "/:/resources/artist.png", - "key": "9", - "type": "artist", - "title": "Music", - "agent": "tv.plex.agents.music", - "scanner": "Plex Music", - "language": "en-US", - "uuid": "e5f67890a1b2c3d4", - "updatedAt": 1684974922, - "createdAt": 1598476740, - "scannedAt": 1893047140, - "content": true, - "directory": true, - "contentChangedAt": 7204063, - "hidden": 0, - "Location": [ - { - "id": 24, - "path": "/Music" - } - ] - }, - { - "allowSync": true, - "art": "/:/resources/movie-fanart.jpg", - "composite": "/library/sections/5/composite/1893047123", - "filters": true, - "refreshing": false, - "thumb": "/:/resources/video.png", - "key": "5", - "type": "movie", - "title": "Graduation", - "agent": "com.plexapp.agents.none", - "scanner": "Plex Video Files Scanner", - "language": "xn", - "uuid": "f67890a1b2c3d4e5", - "updatedAt": 1684974733, - "createdAt": 1598475949, - "scannedAt": 1893047123, - "content": true, - "directory": true, - "contentChangedAt": 3828909, - "hidden": 0, - "Location": [ - { - "id": 14, - "path": "/Plex Library/Conspiracy" - } - ] - } - ] - } - }; +describe("GET /library/sections", () => { + it("should validate the 200 response when the API spec is valid", () => { + const response = { + MediaContainer: { + size: 14, + allowSync: false, + title1: "Plex Library", + Directory: [ + { + allowSync: true, + art: "/:/resources/movie-fanart.jpg", + composite: "/library/sections/7/composite/1893047123", + filters: true, + refreshing: false, + thumb: "/:/resources/movie.png", + key: "7", + type: "movie", + title: "Kids Movies", + agent: "tv.plex.agents.movie", + scanner: "Plex Movie", + language: "en-US", + uuid: "a1b2c3d4e5f67890", + updatedAt: 1728394001, + createdAt: 1598476504, + scannedAt: 1893047123, + content: true, + directory: true, + contentChangedAt: 4738921, + hidden: 0, + Location: [ + { + id: 25, + path: "/KidsMovies" + } + ] + }, + { + allowSync: true, + art: "/:/resources/movie-fanart.jpg", + composite: "/library/sections/13/composite/1893047124", + filters: true, + refreshing: false, + thumb: "/:/resources/movie.png", + key: "13", + type: "movie", + title: "Kids Movies NL", + agent: "tv.plex.agents.movie", + scanner: "Plex Movie", + language: "nl-NL", + uuid: "b2c3d4e5f67890a1", + updatedAt: 1680007500, + createdAt: 1680007500, + scannedAt: 1893047124, + content: true, + directory: true, + contentChangedAt: 5283714, + hidden: 0, + Location: [ + { + id: 23, + path: "/KidsMoviesNL" + } + ] + }, + { + allowSync: true, + art: "/:/resources/movie-fanart.jpg", + composite: "/library/sections/1/composite/1893047130", + filters: true, + refreshing: false, + thumb: "/:/resources/movie.png", + key: "1", + type: "movie", + title: "Movies", + agent: "tv.plex.agents.movie", + scanner: "Plex Movie", + language: "en-US", + uuid: "c3d4e5f67890a1b2", + updatedAt: 1728394005, + createdAt: 1598476200, + scannedAt: 1893047130, + content: true, + directory: true, + contentChangedAt: 6379184, + hidden: 0, + Location: [ + { + id: 18, + path: "/Movies" + } + ] + }, + { + allowSync: true, + art: "/:/resources/movie-fanart.jpg", + composite: "/library/sections/6/composite/1893047135", + filters: true, + refreshing: false, + thumb: "/:/resources/movie.png", + key: "6", + type: "movie", + title: "Movies (Documentaries)", + agent: "tv.plex.agents.movie", + scanner: "Plex Movie", + language: "en-US", + uuid: "d4e5f67890a1b2c3", + updatedAt: 1728394010, + createdAt: 1598476302, + scannedAt: 1893047135, + content: true, + directory: true, + contentChangedAt: 5293874, + hidden: 0, + Location: [ + { + id: 29, + path: "/Movies (Documentaries)" + }, + { + id: 15, + path: "/Plex Library/Documentaries" + } + ] + }, + { + allowSync: true, + art: "/:/resources/movie-fanart.jpg", + composite: "/library/sections/15/composite/1893047145", + filters: true, + refreshing: false, + thumb: "/:/resources/movie.png", + key: "15", + type: "movie", + title: "Test Media Movies", + agent: "tv.plex.agents.movie", + scanner: "Plex Movie", + language: "en-US", + uuid: "e5f67890a1b2c3d4", + updatedAt: 1689075000, + createdAt: 1689075000, + scannedAt: 1893047145, + content: true, + directory: true, + contentChangedAt: 5182738, + hidden: 0, + Location: [ + { + id: 27, + path: "/TestMedia/Movies" + } + ] + }, + { + allowSync: true, + art: "/:/resources/show-fanart.jpg", + composite: "/library/sections/3/composite/1893047110", + filters: true, + refreshing: false, + thumb: "/:/resources/show.png", + key: "3", + type: "show", + title: "Anime", + agent: "com.plexapp.agents.hama", + scanner: "Plex Series Scanner", + language: "en", + uuid: "f67890a1b2c3d4e5", + updatedAt: 1684970001, + createdAt: 1598476000, + scannedAt: 1893047110, + content: true, + directory: true, + contentChangedAt: 8379201, + hidden: 0, + Location: [ + { + id: 17, + path: "/Anime" + } + ] + }, + { + allowSync: true, + art: "/:/resources/show-fanart.jpg", + composite: "/library/sections/12/composite/1893047125", + filters: true, + refreshing: false, + thumb: "/:/resources/show.png", + key: "12", + type: "show", + title: "Kids Tv Shows NL", + agent: "tv.plex.agents.series", + scanner: "Plex TV Series", + language: "nl-NL", + uuid: "67890a1b2c3d4e5f", + updatedAt: 1728394002, + createdAt: 1680007400, + scannedAt: 1893047125, + content: true, + directory: true, + contentChangedAt: 5948203, + hidden: 0, + Location: [ + { + id: 22, + path: "/KidsTvShowsNL" + } + ] + }, + { + allowSync: true, + art: "/:/resources/show-fanart.jpg", + composite: "/library/sections/14/composite/1893047145", + filters: true, + refreshing: false, + thumb: "/:/resources/show.png", + key: "14", + type: "show", + title: "Reality TV (NL)", + agent: "tv.plex.agents.series", + scanner: "Plex TV Series", + language: "nl-NL", + uuid: "890a1b2c3d4e5f67", + updatedAt: 1728394007, + createdAt: 1601860600, + scannedAt: 1893047145, + content: true, + directory: true, + contentChangedAt: 6283720, + hidden: 0, + Location: [ + { + id: 26, + path: "/Reality TV NL" + } + ] + }, + { + allowSync: true, + art: "/:/resources/show-fanart.jpg", + composite: "/library/sections/2/composite/1893047150", + filters: true, + refreshing: false, + thumb: "/:/resources/show.png", + key: "2", + type: "show", + title: "TV Series ", + agent: "tv.plex.agents.series", + scanner: "Plex TV Series", + language: "en-US", + uuid: "a1b2c3d4e5f67890", + updatedAt: 1728394003, + createdAt: 1598476100, + scannedAt: 1893047150, + content: true, + directory: true, + contentChangedAt: 6472184, + hidden: 0, + Location: [ + { + id: 32, + path: "/TV Shows" + } + ] + }, + { + allowSync: true, + art: "/:/resources/show-fanart.jpg", + composite: "/library/sections/16/composite/1893047155", + filters: true, + refreshing: false, + thumb: "/:/resources/show.png", + key: "16", + type: "show", + title: "TV Shows (Documentaries)", + agent: "tv.plex.agents.series", + scanner: "Plex TV Series", + language: "en-US", + uuid: "b2c3d4e5f67890a1", + updatedAt: 1689076000, + createdAt: 1689076000, + scannedAt: 1893047155, + content: true, + directory: true, + contentChangedAt: 4920835, + hidden: 0, + Location: [ + { + id: 28, + path: "/TV Shows (Documentaries)" + } + ] + }, + { + allowSync: true, + art: "/:/resources/show-fanart.jpg", + composite: "/library/sections/17/composite/1893047155", + filters: true, + refreshing: false, + thumb: "/:/resources/show.png", + key: "17", + type: "show", + title: "TV Shows (Kids)", + agent: "tv.plex.agents.series", + scanner: "Plex TV Series", + language: "en-US", + uuid: "c3d4e5f67890a1b2", + updatedAt: 1689077000, + createdAt: 1689077000, + scannedAt: 1893047155, + content: true, + directory: true, + contentChangedAt: 5309283, + hidden: 0, + Location: [ + { + id: 31, + path: "/TV Shows (Kids)" + } + ] + }, + { + allowSync: true, + art: "/:/resources/show-fanart.jpg", + composite: "/library/sections/10/composite/1893047170", + filters: true, + refreshing: false, + thumb: "/:/resources/show.png", + key: "10", + type: "show", + title: "TV Shows (Reality)", + agent: "tv.plex.agents.series", + scanner: "Plex TV Series", + language: "en-US", + uuid: "d4e5f67890a1b2c3", + updatedAt: 1689078000, + createdAt: 1626704821, + scannedAt: 1893047170, + content: true, + directory: true, + contentChangedAt: 7291885, + hidden: 0, + Location: [ + { + id: 30, + path: "/TV Shows (Reality)" + } + ] + }, + { + allowSync: true, + art: "/:/resources/artist-fanart.jpg", + composite: "/library/sections/9/composite/1893047140", + filters: true, + refreshing: false, + thumb: "/:/resources/artist.png", + key: "9", + type: "artist", + title: "Music", + agent: "tv.plex.agents.music", + scanner: "Plex Music", + language: "en-US", + uuid: "e5f67890a1b2c3d4", + updatedAt: 1684974922, + createdAt: 1598476740, + scannedAt: 1893047140, + content: true, + directory: true, + contentChangedAt: 7204063, + hidden: 0, + Location: [ + { + id: 24, + path: "/Music" + } + ] + }, + { + allowSync: true, + art: "/:/resources/movie-fanart.jpg", + composite: "/library/sections/5/composite/1893047123", + filters: true, + refreshing: false, + thumb: "/:/resources/video.png", + key: "5", + type: "movie", + title: "Graduation", + agent: "com.plexapp.agents.none", + scanner: "Plex Video Files Scanner", + language: "xn", + uuid: "f67890a1b2c3d4e5", + updatedAt: 1684974733, + createdAt: 1598475949, + scannedAt: 1893047123, + content: true, + directory: true, + contentChangedAt: 3828909, + hidden: 0, + Location: [ + { + id: 14, + path: "/Plex Library/Conspiracy" + } + ] + } + ] + } + } - validateResponseSpec("/library/sections", "get", 200, response) - }); + validateResponseSpec("/library/sections", "get", 200, response) + }) }) diff --git a/tests/paths/media/providers/get-media-providers.spec.ts b/tests/paths/media/providers/get-media-providers.spec.ts index 6c9f9829..f50b537c 100644 --- a/tests/paths/media/providers/get-media-providers.spec.ts +++ b/tests/paths/media/providers/get-media-providers.spec.ts @@ -1,221 +1,222 @@ -import {validateResponseSpec} from "@utils"; -import {describe, it} from 'vitest' +import { validateResponseSpec } from "@utils" +import { describe, it } from "vitest" -describe('GET /media/providers', () => { - it('should validate the 200 response when the API spec is valid', () => { - const response = { - "MediaContainer": { - "size": 7, - "allowCameraUpload": false, - "allowChannelAccess": false, - "allowSharing": true, - "allowSync": true, - "allowTuners": false, - "backgroundProcessing": true, - "certificate": true, - "companionProxy": true, - "countryCode": "uk", - "diagnostics": "streaminglogs,databases", - "eventStream": true, - "friendlyName": "desktop-titan", - "livetv": 12, - "machineIdentifier": "cf18e74b-7e92-403f-b95a-a99e1f83f77b", - "musicAnalysis": 3, - "myPlex": true, - "myPlexMappingState": "linked", - "myPlexSigninState": "active", - "myPlexSubscription": true, - "myPlexUsername": "random.jason@outlook.com", - "offlineTranscode": 4, - "ownerFeatures": "5f36ef7d-bf9e-48a4-9399-6fddf0ea47b8,2e5687c9-9e71-4712-aec5-0198a93ff56f,3b84a6a9-bcfa-438b-b732-62cb3fcb6732,7d197f42-dc48-4e7b-a758-b6d52ffb216d", - "platform": "Linux", - "platformVersion": "18.04 (Build 12345)", - "pluginHost": true, - "pushNotifications": true, - "readOnlyLibraries": true, - "streamingBrainABRVersion": 5, - "streamingBrainVersion": 4, - "sync": true, - "transcoderActiveVideoSessions": 2, - "transcoderAudio": true, - "transcoderLyrics": true, - "transcoderSubtitles": true, - "transcoderVideo": true, - "transcoderVideoBitrates": "150,250,350,500,1000,2000,4000", - "transcoderVideoQualities": "2,3,5,7,9", - "transcoderVideoResolutions": "160,320,480,720,1080", - "updatedAt": 1835421007, - "updater": true, - "version": "3.40.9.1536-745a67c45", - "voiceSearch": true, - "MediaProvider": [ - { - "identifier": "com.plexapp.plugins.library", - "title": "Random Library", - "types": "photo,audio,video", - "protocols": "stream,http", - "Feature": [ - { - "key": "/library/sections/new", - "type": "content", - "Directory": [ - { - "hubKey": "/hubs/home", - "title": "Main Hub" - }, - { - "agent": "tv.plex.agents.movie", - "language": "fr-FR", - "refreshing": false, - "scanner": "Random Movie Scanner", - "uuid": "9b8e23c4-592c-4d3d-b2b6-7e5d1843c7a8", - "id": "5", - "key": "/library/sections/5", - "hubKey": "/hubs/sections/5", - "type": "movie", - "title": "Films", - "updatedAt": 1835420739, - "scannedAt": 1835420123, - "Pivot": [ - { - "id": "recommended", - "key": "/hubs/sections/5", - "type": "hub", - "title": "Popular", - "context": "content.popular", - "symbol": "star" - }, - { - "id": "library", - "key": "/library/sections/5/all?type=5", - "type": "list", - "title": "Film Library", - "context": "content.library", - "symbol": "library" - }, - { - "id": "categories", - "key": "/library/sections/5/categories", - "type": "list", - "title": "Film Categories", - "context": "content.categories", - "symbol": "stack" - } - ] - }, - { - "agent": "tv.plex.agents.series", - "language": "de-DE", - "refreshing": false, - "scanner": "Random TV Series Scanner", - "uuid": "af5832e1-3246-4e31-9f48-b24f670d95d7", - "id": "6", - "key": "/library/sections/6", - "hubKey": "/hubs/sections/6", - "type": "show", - "title": "Shows", - "updatedAt": 1835413943, - "scannedAt": 1835413688, - "Pivot": [ - { - "id": "trending", - "key": "/hubs/sections/6", - "type": "hub", - "title": "Trending Shows", - "context": "content.trending", - "symbol": "fire" - }, - { - "id": "library", - "key": "/library/sections/6/all?type=6", - "type": "list", - "title": "TV Library", - "context": "content.library", - "symbol": "library" - }, - { - "id": "categories", - "key": "/library/sections/6/categories", - "type": "list", - "title": "Show Categories", - "context": "content.categories", - "symbol": "stack" - } - ] - } - ] - }, - { - "key": "/hubs/search/new", - "type": "search" - }, - { - "key": "/library/matches/new", - "type": "match" - }, - { - "key": "/library/metadata/new", - "type": "metadata" - }, - { - "key": "/photo/:/transcode", - "type": "imagetranscoder" - }, - { - "key": "/hubs/promoted/new", - "type": "promoted" - }, - { - "key": "/hubs/continueWatching/new", - "type": "continuewatching" - }, - { - "key": "/actions/new", - "type": "actions", - "Action": [ - { - "id": "addToContinueWatching", - "key": "/actions/addToContinueWatching" - } - ] - }, - { - "flavor": "global", - "key": "/playlists/new", - "type": "playlist" - }, - { - "flavor": "global", - "key": "/playQueues/new", - "type": "playqueue" - }, - { - "key": "/library/collections/new", - "type": "collection" - }, - { - "scrobbleKey": "/:/scrobble/new", - "unscrobbleKey": "/:/unscrobble/new", - "key": "/:/timeline/new", - "type": "timeline" - }, - { - "type": "queryParser" - }, - { - "flavor": "global", - "type": "subscribe" - }, - { - "key": "/library/search/new", - "type": "universalsearch" - } - ] - } +describe("GET /media/providers", () => { + it("should validate the 200 response when the API spec is valid", () => { + const response = { + MediaContainer: { + size: 7, + allowCameraUpload: false, + allowChannelAccess: false, + allowSharing: true, + allowSync: true, + allowTuners: false, + backgroundProcessing: true, + certificate: true, + companionProxy: true, + countryCode: "uk", + diagnostics: "streaminglogs,databases", + eventStream: true, + friendlyName: "desktop-titan", + livetv: 12, + machineIdentifier: "cf18e74b-7e92-403f-b95a-a99e1f83f77b", + musicAnalysis: 3, + myPlex: true, + myPlexMappingState: "linked", + myPlexSigninState: "active", + myPlexSubscription: true, + myPlexUsername: "random.jason@outlook.com", + offlineTranscode: 4, + ownerFeatures: + "5f36ef7d-bf9e-48a4-9399-6fddf0ea47b8,2e5687c9-9e71-4712-aec5-0198a93ff56f,3b84a6a9-bcfa-438b-b732-62cb3fcb6732,7d197f42-dc48-4e7b-a758-b6d52ffb216d", + platform: "Linux", + platformVersion: "18.04 (Build 12345)", + pluginHost: true, + pushNotifications: true, + readOnlyLibraries: true, + streamingBrainABRVersion: 5, + streamingBrainVersion: 4, + sync: true, + transcoderActiveVideoSessions: 2, + transcoderAudio: true, + transcoderLyrics: true, + transcoderSubtitles: true, + transcoderVideo: true, + transcoderVideoBitrates: "150,250,350,500,1000,2000,4000", + transcoderVideoQualities: "2,3,5,7,9", + transcoderVideoResolutions: "160,320,480,720,1080", + updatedAt: 1835421007, + updater: true, + version: "3.40.9.1536-745a67c45", + voiceSearch: true, + MediaProvider: [ + { + identifier: "com.plexapp.plugins.library", + title: "Random Library", + types: "photo,audio,video", + protocols: "stream,http", + Feature: [ + { + key: "/library/sections/new", + type: "content", + Directory: [ + { + hubKey: "/hubs/home", + title: "Main Hub" + }, + { + agent: "tv.plex.agents.movie", + language: "fr-FR", + refreshing: false, + scanner: "Random Movie Scanner", + uuid: "9b8e23c4-592c-4d3d-b2b6-7e5d1843c7a8", + id: "5", + key: "/library/sections/5", + hubKey: "/hubs/sections/5", + type: "movie", + title: "Films", + updatedAt: 1835420739, + scannedAt: 1835420123, + Pivot: [ + { + id: "recommended", + key: "/hubs/sections/5", + type: "hub", + title: "Popular", + context: "content.popular", + symbol: "star" + }, + { + id: "library", + key: "/library/sections/5/all?type=5", + type: "list", + title: "Film Library", + context: "content.library", + symbol: "library" + }, + { + id: "categories", + key: "/library/sections/5/categories", + type: "list", + title: "Film Categories", + context: "content.categories", + symbol: "stack" + } ] - } - } ; + }, + { + agent: "tv.plex.agents.series", + language: "de-DE", + refreshing: false, + scanner: "Random TV Series Scanner", + uuid: "af5832e1-3246-4e31-9f48-b24f670d95d7", + id: "6", + key: "/library/sections/6", + hubKey: "/hubs/sections/6", + type: "show", + title: "Shows", + updatedAt: 1835413943, + scannedAt: 1835413688, + Pivot: [ + { + id: "trending", + key: "/hubs/sections/6", + type: "hub", + title: "Trending Shows", + context: "content.trending", + symbol: "fire" + }, + { + id: "library", + key: "/library/sections/6/all?type=6", + type: "list", + title: "TV Library", + context: "content.library", + symbol: "library" + }, + { + id: "categories", + key: "/library/sections/6/categories", + type: "list", + title: "Show Categories", + context: "content.categories", + symbol: "stack" + } + ] + } + ] + }, + { + key: "/hubs/search/new", + type: "search" + }, + { + key: "/library/matches/new", + type: "match" + }, + { + key: "/library/metadata/new", + type: "metadata" + }, + { + key: "/photo/:/transcode", + type: "imagetranscoder" + }, + { + key: "/hubs/promoted/new", + type: "promoted" + }, + { + key: "/hubs/continueWatching/new", + type: "continuewatching" + }, + { + key: "/actions/new", + type: "actions", + Action: [ + { + id: "addToContinueWatching", + key: "/actions/addToContinueWatching" + } + ] + }, + { + flavor: "global", + key: "/playlists/new", + type: "playlist" + }, + { + flavor: "global", + key: "/playQueues/new", + type: "playqueue" + }, + { + key: "/library/collections/new", + type: "collection" + }, + { + scrobbleKey: "/:/scrobble/new", + unscrobbleKey: "/:/unscrobble/new", + key: "/:/timeline/new", + type: "timeline" + }, + { + type: "queryParser" + }, + { + flavor: "global", + type: "subscribe" + }, + { + key: "/library/search/new", + type: "universalsearch" + } + ] + } + ] + } + } - validateResponseSpec("/media/providers", "get", 200, response) - }); + validateResponseSpec("/media/providers", "get", 200, response) + }) }) diff --git a/tests/paths/users/post-sign-in.spec.ts b/tests/paths/users/post-sign-in.spec.ts index 83a02d57..ea4bb4c7 100644 --- a/tests/paths/users/post-sign-in.spec.ts +++ b/tests/paths/users/post-sign-in.spec.ts @@ -1,463 +1,545 @@ -import {validateResponseSpec} from "../../utils/"; -import {describe, expect, it} from 'vitest' +import { validateResponseSpec } from "../../utils/" +import { describe, expect, it } from "vitest" -describe('POST /users/signin', () => { - it('should validate the 201 response when the API spec is valid', () => { - const response = { - "id": 62735028, - "uuid": "b9h6f3c8k1", - "username": "ZjFvKLCb", - "title": "ZjFvKLCb", - "email": "c9eMdShR@example.com", - "friendlyName": "", - "locale": null, - "confirmed": false, - "joinedAt": 1552593531, - "emailOnlyAuth": false, - "hasPassword": true, - "protected": false, - "thumb": "https://plex.tv/users/b9h6f3c8k1/avatar?c=1724402119", - "authToken": "jx9nTbVbHk1gM", - "mailingListStatus": "unsubscribed", - "mailingListActive": false, - "scrobbleTypes": "", - "country": "ES", - "subscription": { - "active": false, - "subscribedAt": "2019-03-16T15:20:12Z", - "status": "Inactive", - "paymentService": null, - "plan": null, - "features": ["guided-upgrade", "increase-password-complexity", "upgrade-3ds2", "ad-countdown-timer", "adaptive_bitrate", "amazon-loop-debug", "Android - Dolby Vision", "Android - PiP", "avod-ad-analysis", "avod-new-media", "blacklist_get_signin", "CU Sunset", "client-radio-stations", "cloudflare-turnstile-required", "comments_and_replies_push_notifications", "friend_request_push_notifications", "community_access_plex_tv", "companions_sonos", "custom-home-removal", "disable_home_user_friendships", "disable_sharing_friendships", "drm_support", "le_isrg_root_x1", "federated-auth", "home", "HRK_enable_EUR", "ios14-privacy-banner", "iterable-notification-tokens", "keep-payment-method", "kevin-bacon", "korea-consent", "lets_encrypt", "lightning-dvr-pivot", "livetv", "live-tv-support-incomplete-segments", "tuner-sharing", "metadata_search", "vod_cloudflare", "new_plex_pass_prices", "news-provider-sunset-modal", "photos-favorites", "photos-metadata-edition", "pms_health", "rate-limit-client-token", "shared_server_notification", "shared_source_notification", "scrobbling-service-plex-tv", "collections", "radio", "exclude restrictions", "signin_with_apple", "spring_serve_ad_provider", "transcoder_cache", "TREBLE-show-features", "two-factor-authentication", "unsupportedtuners", "vod-schema", "watch-together-invite", "web_server_dashboard"] - }, - "subscriptionDescription": null, - "restricted": false, - "anonymous": false, - "home": false, - "guest": false, - "homeSize": 1, - "homeAdmin": false, - "maxHomeSize": 15, - "rememberExpiresAt": 1725611719, - "profile": { - "autoSelectAudio": true, - "defaultAudioLanguage": "ja", - "defaultSubtitleLanguage": "en", - "autoSelectSubtitle": 1, - "defaultSubtitleAccessibility": 0, - "defaultSubtitleForced": 0, - "watchedIndicator": 1, - "mediaReviewsVisibility": 0 - }, - "entitlements": [], - "subscriptions": [], - "pastSubscriptions": [{ - "id": null, - "mode": null, - "renewsAt": null, - "endsAt": 1556281940, - "billing": { - "paymentMethodId": null, "internalPaymentMethod": {} - }, - "canceled": false, - "gracePeriod": false, - "onHold": false, - "canReactivate": false, - "canUpgrade": false, - "canDowngrade": false, - "canConvert": false, - "type": "plexpass", - "transfer": null, - "state": "ended" - }], - "trials": [], - "services": [{ - "identifier": "epg", - "endpoint": "https://epg.provider.plex.tv", - "token": "d2h7zUjKgT5oLwQ3cQ6d", - "secret": null, - "status": "online" - }, { - "identifier": "epg-staging", - "endpoint": "https://epg-staging.provider.plex.tv", - "token": "r7m1nVfPhU4eRwM7eR5a", - "secret": null, - "status": "online" - }, { - "identifier": "epg-dev", - "endpoint": "https://epg-dev.provider.plex.tv", - "token": "z4w2sLxPbJ3iRkY2hN8f", - "secret": null, - "status": "online" - }, { - "identifier": "eyeq", - "endpoint": "https://c4412416.ipg.web.cddbp.net/webapi/xml/1.0/", - "token": "g1v9kWxHdT3jNiY6dF4b", - "secret": null, - "status": "online" - }, { - "identifier": "eyeq-channel-icons", - "endpoint": "http://akamai-b.cdn.cddbp.net/cds/2.0/image", - "token": null, - "secret": null, - "status": "online" - }, { - "identifier": "graph-dev", - "endpoint": "https://community-dev.plex.tv", - "token": null, - "secret": null, - "status": "online" - }, { - "identifier": "graph-staging", - "endpoint": "https://community-staging.plex.tv", - "token": null, - "secret": null, - "status": "online" - }, { - "identifier": "community-dev", - "endpoint": "https://community-dev.plex.tv", - "token": null, - "secret": null, - "status": "online" - }, { - "identifier": "community-staging", - "endpoint": "https://community-staging.plex.tv", - "token": null, - "secret": null, - "status": "online" - }, { - "identifier": "community", - "endpoint": "https://community.plex.tv", - "token": null, - "secret": null, - "status": "online" - }, { - "identifier": "metadata", - "endpoint": "https://metadata.provider.plex.tv", - "token": "h5z3rQxLhJ2lRyB4gN7v", - "secret": null, - "status": "online" - }, { - "identifier": "scrobbling", - "endpoint": "https://scrobbles.plex.tv", - "token": "s6y1wTxFpL7jMxC3nR8e", - "secret": null, - "status": "online" - }, { - "identifier": "metadata-dev", - "endpoint": "https://metadata-dev.provider.plex.tv", - "token": "j2h4tWmNpO3lRyD7vF9x", - "secret": null, - "status": "online" - }, { - "identifier": "metadata-provider", - "endpoint": "https://mpm.plex.tv/", - "token": null, - "secret": null, - "status": "online" - }, { - "identifier": "tmsapi", - "endpoint": "https://tmsapi.plex.tv/v1.1/", - "token": "p7c8xVhLpM5oTzQ1wR2b", - "secret": null, - "status": "online" - }, { - "identifier": "subtitles-search", - "endpoint": "https://metadata.provider.plex.tv/library/streams/matches", - "token": "t8f3kWqNhP6uLyX4mC2y", - "secret": null, - "status": "online" - }, { - "identifier": "acoustid", - "endpoint": "https://acoustid.plex.tv/", - "token": "l5n7rQxHpV2oRyD9gF6b", - "secret": null, - "status": "online" - }, { - "identifier": "lyricfind", - "endpoint": "https://lyricfind.plex.tv/", - "token": "m3y4tUjKlN8fMxC7oQ2b", - "secret": "a1d5vWxHpQ6oLyT4gR3c", - "status": "online" - }, { - "identifier": "lyricfind-search", - "endpoint": "https://lyricfind.plex.tv/", - "token": "q6c8wTxFpO9kLrZ2hV5b", - "secret": null, - "status": "online" - }, { - "identifier": "tvdb", - "endpoint": "https://api4.thetvdb.com/", - "token": "r4h7zUjMgL1fTzN3yP5x", - "secret": null, - "status": "online" - }], - "adsConsent": null, - "adsConsentSetAt": null, - "adsConsentReminderAt": null, - "experimentalFeatures": false, - "twoFactorEnabled": false, - "backupCodesCreated": false, - "attributionPartner": null - }; +describe("POST /users/signin", () => { + it("should validate the 201 response when the API spec is valid", () => { + const response = { + id: 62735028, + uuid: "b9h6f3c8k1", + username: "ZjFvKLCb", + title: "ZjFvKLCb", + email: "c9eMdShR@example.com", + friendlyName: "", + locale: null, + confirmed: false, + joinedAt: 1552593531, + emailOnlyAuth: false, + hasPassword: true, + protected: false, + thumb: "https://plex.tv/users/b9h6f3c8k1/avatar?c=1724402119", + authToken: "jx9nTbVbHk1gM", + mailingListStatus: "unsubscribed", + mailingListActive: false, + scrobbleTypes: "", + country: "ES", + subscription: { + active: false, + subscribedAt: "2019-03-16T15:20:12Z", + status: "Inactive", + paymentService: null, + plan: null, + features: [ + "guided-upgrade", + "increase-password-complexity", + "upgrade-3ds2", + "ad-countdown-timer", + "adaptive_bitrate", + "amazon-loop-debug", + "Android - Dolby Vision", + "Android - PiP", + "avod-ad-analysis", + "avod-new-media", + "blacklist_get_signin", + "CU Sunset", + "client-radio-stations", + "cloudflare-turnstile-required", + "comments_and_replies_push_notifications", + "friend_request_push_notifications", + "community_access_plex_tv", + "companions_sonos", + "custom-home-removal", + "disable_home_user_friendships", + "disable_sharing_friendships", + "drm_support", + "le_isrg_root_x1", + "federated-auth", + "home", + "HRK_enable_EUR", + "ios14-privacy-banner", + "iterable-notification-tokens", + "keep-payment-method", + "kevin-bacon", + "korea-consent", + "lets_encrypt", + "lightning-dvr-pivot", + "livetv", + "live-tv-support-incomplete-segments", + "tuner-sharing", + "metadata_search", + "vod_cloudflare", + "new_plex_pass_prices", + "news-provider-sunset-modal", + "photos-favorites", + "photos-metadata-edition", + "pms_health", + "rate-limit-client-token", + "shared_server_notification", + "shared_source_notification", + "scrobbling-service-plex-tv", + "collections", + "radio", + "exclude restrictions", + "signin_with_apple", + "spring_serve_ad_provider", + "transcoder_cache", + "TREBLE-show-features", + "two-factor-authentication", + "unsupportedtuners", + "vod-schema", + "watch-together-invite", + "web_server_dashboard" + ] + }, + subscriptionDescription: null, + restricted: false, + anonymous: false, + home: false, + guest: false, + homeSize: 1, + homeAdmin: false, + maxHomeSize: 15, + rememberExpiresAt: 1725611719, + profile: { + autoSelectAudio: true, + defaultAudioLanguage: "ja", + defaultSubtitleLanguage: "en", + autoSelectSubtitle: 1, + defaultSubtitleAccessibility: 0, + defaultSubtitleForced: 0, + watchedIndicator: 1, + mediaReviewsVisibility: 0 + }, + entitlements: [], + subscriptions: [], + pastSubscriptions: [ + { + id: null, + mode: null, + renewsAt: null, + endsAt: 1556281940, + billing: { + paymentMethodId: null, + internalPaymentMethod: {} + }, + canceled: false, + gracePeriod: false, + onHold: false, + canReactivate: false, + canUpgrade: false, + canDowngrade: false, + canConvert: false, + type: "plexpass", + transfer: null, + state: "ended" + } + ], + trials: [], + services: [ + { + identifier: "epg", + endpoint: "https://epg.provider.plex.tv", + token: "d2h7zUjKgT5oLwQ3cQ6d", + secret: null, + status: "online" + }, + { + identifier: "epg-staging", + endpoint: "https://epg-staging.provider.plex.tv", + token: "r7m1nVfPhU4eRwM7eR5a", + secret: null, + status: "online" + }, + { + identifier: "epg-dev", + endpoint: "https://epg-dev.provider.plex.tv", + token: "z4w2sLxPbJ3iRkY2hN8f", + secret: null, + status: "online" + }, + { + identifier: "eyeq", + endpoint: "https://c4412416.ipg.web.cddbp.net/webapi/xml/1.0/", + token: "g1v9kWxHdT3jNiY6dF4b", + secret: null, + status: "online" + }, + { + identifier: "eyeq-channel-icons", + endpoint: "http://akamai-b.cdn.cddbp.net/cds/2.0/image", + token: null, + secret: null, + status: "online" + }, + { + identifier: "graph-dev", + endpoint: "https://community-dev.plex.tv", + token: null, + secret: null, + status: "online" + }, + { + identifier: "graph-staging", + endpoint: "https://community-staging.plex.tv", + token: null, + secret: null, + status: "online" + }, + { + identifier: "community-dev", + endpoint: "https://community-dev.plex.tv", + token: null, + secret: null, + status: "online" + }, + { + identifier: "community-staging", + endpoint: "https://community-staging.plex.tv", + token: null, + secret: null, + status: "online" + }, + { + identifier: "community", + endpoint: "https://community.plex.tv", + token: null, + secret: null, + status: "online" + }, + { + identifier: "metadata", + endpoint: "https://metadata.provider.plex.tv", + token: "h5z3rQxLhJ2lRyB4gN7v", + secret: null, + status: "online" + }, + { + identifier: "scrobbling", + endpoint: "https://scrobbles.plex.tv", + token: "s6y1wTxFpL7jMxC3nR8e", + secret: null, + status: "online" + }, + { + identifier: "metadata-dev", + endpoint: "https://metadata-dev.provider.plex.tv", + token: "j2h4tWmNpO3lRyD7vF9x", + secret: null, + status: "online" + }, + { + identifier: "metadata-provider", + endpoint: "https://mpm.plex.tv/", + token: null, + secret: null, + status: "online" + }, + { + identifier: "tmsapi", + endpoint: "https://tmsapi.plex.tv/v1.1/", + token: "p7c8xVhLpM5oTzQ1wR2b", + secret: null, + status: "online" + }, + { + identifier: "subtitles-search", + endpoint: "https://metadata.provider.plex.tv/library/streams/matches", + token: "t8f3kWqNhP6uLyX4mC2y", + secret: null, + status: "online" + }, + { + identifier: "acoustid", + endpoint: "https://acoustid.plex.tv/", + token: "l5n7rQxHpV2oRyD9gF6b", + secret: null, + status: "online" + }, + { + identifier: "lyricfind", + endpoint: "https://lyricfind.plex.tv/", + token: "m3y4tUjKlN8fMxC7oQ2b", + secret: "a1d5vWxHpQ6oLyT4gR3c", + status: "online" + }, + { + identifier: "lyricfind-search", + endpoint: "https://lyricfind.plex.tv/", + token: "q6c8wTxFpO9kLrZ2hV5b", + secret: null, + status: "online" + }, + { + identifier: "tvdb", + endpoint: "https://api4.thetvdb.com/", + token: "r4h7zUjMgL1fTzN3yP5x", + secret: null, + status: "online" + } + ], + adsConsent: null, + adsConsentSetAt: null, + adsConsentReminderAt: null, + experimentalFeatures: false, + twoFactorEnabled: false, + backupCodesCreated: false, + attributionPartner: null + } - validateResponseSpec("/users/signin", "post", 201, response) - }); + validateResponseSpec("/users/signin", "post", 201, response) + }) - it('should validate the 201 response when the API spec is valid', () => { - const response = { - "id": 475829302, - "uuid": "a1b2c3d4e5f67890", - "username": "user12345", - "title": "user12345", - "email": "user12345@example.com", - "friendlyName": "", - "locale": null, - "confirmed": false, - "joinedAt": 1633376532, - "emailOnlyAuth": false, - "hasPassword": true, - "protected": false, - "thumb": "https://plex.tv/users/a1b2c3d4e5f67890/avatar?c=1234567890", - "authToken": "gHjKlMnOpQrStUvWxYz", - "mailingListStatus": "active", - "mailingListActive": true, - "scrobbleTypes": "", - "country": "IT", - "subscription": { - "active": false, - "subscribedAt": null, - "status": "Inactive", - "paymentService": null, - "plan": null, - "features": [ - "guided-upgrade", - "increase-password-complexity", - "upgrade-3ds2", - "ad-countdown-timer", - "adaptive_bitrate", - "amazon-loop-debug", - "avod-ad-analysis", - "avod-new-media", - "blacklist_get_signin", - "CU Sunset", - "client-radio-stations", - "cloudflare-turnstile-required", - "comments_and_replies_push_notifications", - "friend_request_push_notifications", - "community_access_plex_tv", - "companions_sonos", - "custom-home-removal", - "disable_home_user_friendships", - "disable_sharing_friendships", - "drm_support", - "le_isrg_root_x1", - "federated-auth", - "home", - "HRK_enable_EUR", - "ios14-privacy-banner", - "iterable-notification-tokens", - "keep-payment-method", - "kevin-bacon", - "korea-consent", - "lets_encrypt", - "lightning-dvr-pivot", - "livetv", - "live-tv-support-incomplete-segments", - "tuner-sharing", - "metadata_search", - "vod_cloudflare", - "new_plex_pass_prices", - "news-provider-sunset-modal", - "photos-favorites", - "photos-metadata-edition", - "pms_health", - "rate-limit-client-token", - "shared_server_notification", - "shared_source_notification", - "scrobbling-service-plex-tv", - "collections", - "radio", - "exclude restrictions", - "signin_with_apple", - "spring_serve_ad_provider", - "transcoder_cache", - "TREBLE-show-features", - "two-factor-authentication", - "unsupportedtuners", - "vod-schema", - "watch-together-invite", - "web_server_dashboard" - ] - }, - "subscriptionDescription": null, - "restricted": false, - "anonymous": null, - "home": false, - "guest": false, - "homeSize": 1, - "homeAdmin": false, - "maxHomeSize": 15, - "rememberExpiresAt": 1725684008, - "profile": { - "autoSelectAudio": true, - "defaultAudioLanguage": null, - "defaultSubtitleLanguage": null, - "autoSelectSubtitle": 1, - "defaultSubtitleAccessibility": 0, - "defaultSubtitleForced": 0, - "watchedIndicator": 1, - "mediaReviewsVisibility": 0 - }, - "entitlements": [], - "subscriptions": [], - "pastSubscriptions": [], - "trials": [], - "services": [ - { - "identifier": "epg", - "endpoint": "https://epg.provider.plex.tv", - "token": "A1B2C3D4E5F6G7H8I9J0", - "secret": null, - "status": "online" - }, - { - "identifier": "epg-staging", - "endpoint": "https://epg-staging.provider.plex.tv", - "token": "J9I8H7G6F5E4D3C2B1A0", - "secret": null, - "status": "online" - }, - { - "identifier": "epg-dev", - "endpoint": "https://epg-dev.provider.plex.tv", - "token": "0J9I8H7G6F5E4D3C2B1A", - "secret": null, - "status": "online" - }, - { - "identifier": "eyeq", - "endpoint": "https://c4412416.ipg.web.cddbp.net/webapi/xml/1.0/", - "token": "Z1X2C3V4B5N6M7Q8W9R0", - "secret": null, - "status": "online" - }, - { - "identifier": "eyeq-channel-icons", - "endpoint": "http://akamai-b.cdn.cddbp.net/cds/2.0/image", - "token": null, - "secret": null, - "status": "online" - }, - { - "identifier": "graph-dev", - "endpoint": "https://community-dev.plex.tv", - "token": null, - "secret": null, - "status": "online" - }, - { - "identifier": "graph-staging", - "endpoint": "https://community-staging.plex.tv", - "token": null, - "secret": null, - "status": "online" - }, - { - "identifier": "community-dev", - "endpoint": "https://community-dev.plex.tv", - "token": null, - "secret": null, - "status": "online" - }, - { - "identifier": "community-staging", - "endpoint": "https://community-staging.plex.tv", - "token": null, - "secret": null, - "status": "online" - }, - { - "identifier": "community", - "endpoint": "https://community.plex.tv", - "token": null, - "secret": null, - "status": "online" - }, - { - "identifier": "metadata", - "endpoint": "https://metadata.provider.plex.tv", - "token": "K1L2M3N4O5P6Q7R8S9T0", - "secret": null, - "status": "online" - }, - { - "identifier": "metadata-dev", - "endpoint": "https://metadata-dev.provider.plex.tv", - "token": "T0S9R8Q7P6O5N4M3L2K1", - "secret": null, - "status": "online" - }, - { - "identifier": "metadata-provider", - "endpoint": "https://mpm.plex.tv/", - "token": null, - "secret": null, - "status": "online" - }, - { - "identifier": "tmsapi", - "endpoint": "https://tmsapi.plex.tv/v1.1/", - "token": "F1G2H3J4K5L6M7N8O9P0", - "secret": null, - "status": "online" - }, - { - "identifier": "subtitles-search", - "endpoint": "https://metadata.provider.plex.tv/library/streams/matches", - "token": "Q1W2E3R4T5Y6U7I8O9P0", - "secret": null, - "status": "online" - }, - { - "identifier": "acoustid", - "endpoint": "https://acoustid.plex.tv/", - "token": "1A2S3D4F5G6H7J8K9L0", - "secret": null, - "status": "online" - }, - { - "identifier": "lyricfind", - "endpoint": "https://lyricfind.plex.tv/", - "token": "Z9X8C7V6B5N4M3Q2W1E0", - "secret": "X1C2V3B4N5M6Q7W8E9R0", - "status": "online" - }, - { - "identifier": "lyricfind-search", - "endpoint": "https://lyricfind.plex.tv/", - "token": "F6D5S4A3J2K1L8P7O9M0", - "secret": null, - "status": "online" - }, - { - "identifier": "tvdb", - "endpoint": "https://api4.thetvdb.com/", - "token": "L1K2J3H4G5F6D7S8A9P0", - "secret": null, - "status": "online" - } - ], - "adsConsent": null, - "adsConsentSetAt": null, - "adsConsentReminderAt": null, - "experimentalFeatures": false, - "twoFactorEnabled": false, - "backupCodesCreated": false, - "attributionPartner": null - } - ; - - validateResponseSpec("/users/signin", "post", 201, response) - }) + it("should validate the 201 response when the API spec is valid", () => { + const response = { + id: 475829302, + uuid: "a1b2c3d4e5f67890", + username: "user12345", + title: "user12345", + email: "user12345@example.com", + friendlyName: "", + locale: null, + confirmed: false, + joinedAt: 1633376532, + emailOnlyAuth: false, + hasPassword: true, + protected: false, + thumb: "https://plex.tv/users/a1b2c3d4e5f67890/avatar?c=1234567890", + authToken: "gHjKlMnOpQrStUvWxYz", + mailingListStatus: "active", + mailingListActive: true, + scrobbleTypes: "", + country: "IT", + subscription: { + active: false, + subscribedAt: null, + status: "Inactive", + paymentService: null, + plan: null, + features: [ + "guided-upgrade", + "increase-password-complexity", + "upgrade-3ds2", + "ad-countdown-timer", + "adaptive_bitrate", + "amazon-loop-debug", + "avod-ad-analysis", + "avod-new-media", + "blacklist_get_signin", + "CU Sunset", + "client-radio-stations", + "cloudflare-turnstile-required", + "comments_and_replies_push_notifications", + "friend_request_push_notifications", + "community_access_plex_tv", + "companions_sonos", + "custom-home-removal", + "disable_home_user_friendships", + "disable_sharing_friendships", + "drm_support", + "le_isrg_root_x1", + "federated-auth", + "home", + "HRK_enable_EUR", + "ios14-privacy-banner", + "iterable-notification-tokens", + "keep-payment-method", + "kevin-bacon", + "korea-consent", + "lets_encrypt", + "lightning-dvr-pivot", + "livetv", + "live-tv-support-incomplete-segments", + "tuner-sharing", + "metadata_search", + "vod_cloudflare", + "new_plex_pass_prices", + "news-provider-sunset-modal", + "photos-favorites", + "photos-metadata-edition", + "pms_health", + "rate-limit-client-token", + "shared_server_notification", + "shared_source_notification", + "scrobbling-service-plex-tv", + "collections", + "radio", + "exclude restrictions", + "signin_with_apple", + "spring_serve_ad_provider", + "transcoder_cache", + "TREBLE-show-features", + "two-factor-authentication", + "unsupportedtuners", + "vod-schema", + "watch-together-invite", + "web_server_dashboard" + ] + }, + subscriptionDescription: null, + restricted: false, + anonymous: null, + home: false, + guest: false, + homeSize: 1, + homeAdmin: false, + maxHomeSize: 15, + rememberExpiresAt: 1725684008, + profile: { + autoSelectAudio: true, + defaultAudioLanguage: null, + defaultSubtitleLanguage: null, + autoSelectSubtitle: 1, + defaultSubtitleAccessibility: 0, + defaultSubtitleForced: 0, + watchedIndicator: 1, + mediaReviewsVisibility: 0 + }, + entitlements: [], + subscriptions: [], + pastSubscriptions: [], + trials: [], + services: [ + { + identifier: "epg", + endpoint: "https://epg.provider.plex.tv", + token: "A1B2C3D4E5F6G7H8I9J0", + secret: null, + status: "online" + }, + { + identifier: "epg-staging", + endpoint: "https://epg-staging.provider.plex.tv", + token: "J9I8H7G6F5E4D3C2B1A0", + secret: null, + status: "online" + }, + { + identifier: "epg-dev", + endpoint: "https://epg-dev.provider.plex.tv", + token: "0J9I8H7G6F5E4D3C2B1A", + secret: null, + status: "online" + }, + { + identifier: "eyeq", + endpoint: "https://c4412416.ipg.web.cddbp.net/webapi/xml/1.0/", + token: "Z1X2C3V4B5N6M7Q8W9R0", + secret: null, + status: "online" + }, + { + identifier: "eyeq-channel-icons", + endpoint: "http://akamai-b.cdn.cddbp.net/cds/2.0/image", + token: null, + secret: null, + status: "online" + }, + { + identifier: "graph-dev", + endpoint: "https://community-dev.plex.tv", + token: null, + secret: null, + status: "online" + }, + { + identifier: "graph-staging", + endpoint: "https://community-staging.plex.tv", + token: null, + secret: null, + status: "online" + }, + { + identifier: "community-dev", + endpoint: "https://community-dev.plex.tv", + token: null, + secret: null, + status: "online" + }, + { + identifier: "community-staging", + endpoint: "https://community-staging.plex.tv", + token: null, + secret: null, + status: "online" + }, + { + identifier: "community", + endpoint: "https://community.plex.tv", + token: null, + secret: null, + status: "online" + }, + { + identifier: "metadata", + endpoint: "https://metadata.provider.plex.tv", + token: "K1L2M3N4O5P6Q7R8S9T0", + secret: null, + status: "online" + }, + { + identifier: "metadata-dev", + endpoint: "https://metadata-dev.provider.plex.tv", + token: "T0S9R8Q7P6O5N4M3L2K1", + secret: null, + status: "online" + }, + { + identifier: "metadata-provider", + endpoint: "https://mpm.plex.tv/", + token: null, + secret: null, + status: "online" + }, + { + identifier: "tmsapi", + endpoint: "https://tmsapi.plex.tv/v1.1/", + token: "F1G2H3J4K5L6M7N8O9P0", + secret: null, + status: "online" + }, + { + identifier: "subtitles-search", + endpoint: "https://metadata.provider.plex.tv/library/streams/matches", + token: "Q1W2E3R4T5Y6U7I8O9P0", + secret: null, + status: "online" + }, + { + identifier: "acoustid", + endpoint: "https://acoustid.plex.tv/", + token: "1A2S3D4F5G6H7J8K9L0", + secret: null, + status: "online" + }, + { + identifier: "lyricfind", + endpoint: "https://lyricfind.plex.tv/", + token: "Z9X8C7V6B5N4M3Q2W1E0", + secret: "X1C2V3B4N5M6Q7W8E9R0", + status: "online" + }, + { + identifier: "lyricfind-search", + endpoint: "https://lyricfind.plex.tv/", + token: "F6D5S4A3J2K1L8P7O9M0", + secret: null, + status: "online" + }, + { + identifier: "tvdb", + endpoint: "https://api4.thetvdb.com/", + token: "L1K2J3H4G5F6D7S8A9P0", + secret: null, + status: "online" + } + ], + adsConsent: null, + adsConsentSetAt: null, + adsConsentReminderAt: null, + experimentalFeatures: false, + twoFactorEnabled: false, + backupCodesCreated: false, + attributionPartner: null + } + validateResponseSpec("/users/signin", "post", 201, response) + }) }) diff --git a/tests/utils/import.ts b/tests/utils/import.ts index fa0fa335..1904cce7 100644 --- a/tests/utils/import.ts +++ b/tests/utils/import.ts @@ -1,25 +1,26 @@ -import PMSSpec from '../../output/plex-media-server-spec-dereferenced.yaml'; -import Ajv from "ajv"; -import addFormats from "ajv-formats"; -import {expect} from 'vitest' +import PMSSpec from "../../output/plex-media-server-spec-dereferenced.yaml" +import Ajv from "ajv" +import addFormats from "ajv-formats" +import { expect } from "vitest" +export function validateResponseSpec( + path: string, + type: "get" | "post" | "delete", + statusCode: number, + response: any +): void { + const ajv = new Ajv({ allErrors: true, strict: false }) + ajv.addSchema(PMSSpec, "API.yaml") + addFormats(ajv) -export function validateResponseSpec(path: string, type: 'get' | 'post' | 'delete', statusCode: number, response: any): void { + // Convert JSONPath to JSON Pointer + const pathPointer = `/paths/${path.replace(/\//g, `~1`)}/${type}/responses/${statusCode}/content/application~1json/schema` - const ajv = new Ajv({allErrors: true, strict: false}); - ajv.addSchema(PMSSpec, "API.yaml"); - addFormats(ajv); + const validate = ajv.validate({ $ref: "API.yaml#" + pathPointer }, response) + if (!validate) { + console.error(ajv.errors) + } - // Convert JSONPath to JSON Pointer - const pathPointer = `/paths/${path.replace(/\//g, `~1`)}/${type}/responses/${statusCode}/content/application~1json/schema` - - const validate = ajv.validate({$ref: "API.yaml#" + pathPointer}, response); - - if (!validate) { - console.error(ajv.errors); - } - - expect(validate).toBe(true); - + expect(validate).toBe(true) } diff --git a/tests/utils/index.ts b/tests/utils/index.ts index dd01015f..b27bbd2a 100644 --- a/tests/utils/index.ts +++ b/tests/utils/index.ts @@ -1 +1 @@ -export * from './import.js'; +export * from "./import.js" diff --git a/tsconfig.json b/tsconfig.json index 092dc09d..e501acb2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,16 +4,10 @@ "outDir": "./output", "paths": { "@/*": ["./src/*"], - "@utils": [ - "./tests/utils/index.ts" - ] + "@utils": ["./tests/utils/index.ts"] }, - "types": [ - "@modyfi/vite-plugin-yaml/modules" - ], + "types": ["@modyfi/vite-plugin-yaml/modules"], "skipLibCheck": true }, - "exclude": [ - "node_modules" - ] + "exclude": ["node_modules"] } diff --git a/vite.config.ts b/vite.config.ts index ae4269fe..600515bc 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,7 +1,7 @@ -import ViteYaml from '@modyfi/vite-plugin-yaml'; -import tsconfigPaths from 'vite-tsconfig-paths' -import { defineConfig } from 'vite' +import ViteYaml from "@modyfi/vite-plugin-yaml" +import tsconfigPaths from "vite-tsconfig-paths" +import { defineConfig } from "vite" export default defineConfig({ - plugins: [tsconfigPaths(), ViteYaml()], + plugins: [tsconfigPaths(), ViteYaml()] })