diff --git a/.speakeasy/gen.lock b/.speakeasy/gen.lock index 33d8b82..3827fc6 100755 --- a/.speakeasy/gen.lock +++ b/.speakeasy/gen.lock @@ -1,12 +1,12 @@ lockVersion: 2.0.0 id: 3eeea668-4ef4-464e-a888-bdfa023bedf5 management: - docChecksum: 14b35829d4be91a88269761c3ed01426 + docChecksum: 7dec92ab9151a1b5a35df5faa81811ba docVersion: 0.0.3 - speakeasyVersion: 1.484.1 - generationVersion: 2.503.2 - releaseVersion: 0.21.2 - configChecksum: 8b6dcb5cd25065d9fbc63e5ede1119c9 + speakeasyVersion: 1.487.0 + generationVersion: 2.506.0 + releaseVersion: 0.22.0 + configChecksum: 02e21ec9a82866d44b79d54a1ec21b1d repoURL: https://github.com/LukeHagar/plexpy.git repoSubDirectory: . installationURL: https://github.com/LukeHagar/plexpy.git @@ -15,7 +15,7 @@ features: python: additionalDependencies: 1.0.0 constsAndDefaults: 1.0.5 - core: 5.10.9 + core: 5.11.0 defaultEnabledRetries: 0.2.0 deprecations: 3.0.2 downloadStreams: 1.0.1 @@ -275,6 +275,10 @@ generatedFiles: - docs/models/errors/getuserfriendserrors.md - docs/models/errors/getuserfriendsplexerrors.md - docs/models/errors/getuserfriendsunauthorized.md + - docs/models/errors/getusersbadrequest.md + - docs/models/errors/getuserserrors.md + - docs/models/errors/getusersunauthorized.md + - docs/models/errors/getusersuserserrors.md - docs/models/errors/getwatchlistbadrequest.md - docs/models/errors/getwatchlisterrors.md - docs/models/errors/getwatchlistunauthorized.md @@ -352,6 +356,12 @@ generatedFiles: - docs/models/operations/addplaylistcontentsrequest.md - docs/models/operations/addplaylistcontentsresponse.md - docs/models/operations/addplaylistcontentsresponsebody.md + - docs/models/operations/alllibraries.md + - docs/models/operations/allowcameraupload.md + - docs/models/operations/allowchannels.md + - docs/models/operations/allowsubtitleadmin.md + - docs/models/operations/allowsync.md + - docs/models/operations/allowtuners.md - docs/models/operations/applyupdatesrequest.md - docs/models/operations/applyupdatesresponse.md - docs/models/operations/autoselectsubtitle.md @@ -714,6 +724,7 @@ generatedFiles: - docs/models/operations/gettopwatchedcontentresponse.md - docs/models/operations/gettopwatchedcontentresponsebody.md - docs/models/operations/gettopwatchedcontentrole.md + - docs/models/operations/gettopwatchedcontentuser.md - docs/models/operations/gettranscodesessionsmediacontainer.md - docs/models/operations/gettranscodesessionsresponse.md - docs/models/operations/gettranscodesessionsresponsebody.md @@ -724,11 +735,17 @@ generatedFiles: - docs/models/operations/getupdatestatusresponse.md - docs/models/operations/getupdatestatusresponsebody.md - docs/models/operations/getuserfriendsresponse.md + - docs/models/operations/getusersmediacontainer.md + - docs/models/operations/getusersrequest.md + - docs/models/operations/getusersresponse.md + - docs/models/operations/getusersresponsebody.md + - docs/models/operations/getusersserver.md - docs/models/operations/getwatchlistrequest.md - docs/models/operations/getwatchlistresponse.md - docs/models/operations/getwatchlistresponsebody.md - docs/models/operations/guids.md - docs/models/operations/hasthumbnail.md + - docs/models/operations/home.md - docs/models/operations/hub.md - docs/models/operations/image.md - docs/models/operations/includecollections.md @@ -764,9 +781,11 @@ generatedFiles: - docs/models/operations/onlytransient.md - docs/models/operations/operator.md - docs/models/operations/optimizedforstreaming.md + - docs/models/operations/owned.md - docs/models/operations/part.md - docs/models/operations/pastsubscription.md - docs/models/operations/pathparamtaskname.md + - docs/models/operations/pending.md - docs/models/operations/performsearchrequest.md - docs/models/operations/performsearchresponse.md - docs/models/operations/performvoicesearchrequest.md @@ -794,6 +813,7 @@ generatedFiles: - docs/models/operations/postuserssignindatauserprofile.md - docs/models/operations/postuserssignindatawatchedindicator.md - docs/models/operations/producer.md + - docs/models/operations/protected.md - docs/models/operations/protocol.md - docs/models/operations/provider.md - docs/models/operations/queryparamfilter.md @@ -807,6 +827,7 @@ generatedFiles: - docs/models/operations/ratings.md - docs/models/operations/release.md - docs/models/operations/responsebody.md + - docs/models/operations/restricted.md - docs/models/operations/role.md - docs/models/operations/scope.md - docs/models/operations/searchresult.md @@ -872,6 +893,7 @@ generatedFiles: - docs/sdks/sessions/README.md - docs/sdks/statistics/README.md - docs/sdks/updater/README.md + - docs/sdks/users/README.md - docs/sdks/video/README.md - docs/sdks/watchlist/README.md - poetry.toml @@ -921,6 +943,7 @@ generatedFiles: - src/plex_api_client/models/errors/get_server_identity.py - src/plex_api_client/models/errors/get_server_resources.py - src/plex_api_client/models/errors/get_thumb_image.py + - src/plex_api_client/models/errors/get_users.py - src/plex_api_client/models/errors/get_watch_list.py - src/plex_api_client/models/errors/getavailableclients.py - src/plex_api_client/models/errors/getbandwidthstatistics.py @@ -1001,6 +1024,7 @@ generatedFiles: - src/plex_api_client/models/operations/get_server_identity.py - src/plex_api_client/models/operations/get_server_resources.py - src/plex_api_client/models/operations/get_thumb_image.py + - src/plex_api_client/models/operations/get_users.py - src/plex_api_client/models/operations/get_watch_list.py - src/plex_api_client/models/operations/getavailableclients.py - src/plex_api_client/models/operations/getbandwidthstatistics.py @@ -1066,6 +1090,7 @@ generatedFiles: - src/plex_api_client/types/__init__.py - src/plex_api_client/types/basemodel.py - src/plex_api_client/updater.py + - src/plex_api_client/users.py - src/plex_api_client/utils/__init__.py - src/plex_api_client/utils/annotations.py - src/plex_api_client/utils/enums.py @@ -2093,5 +2118,29 @@ examples: application/json: {"errors": [{"code": 1000, "message": "X-Plex-Client-Identifier is missing", "status": 400}, {"code": 1000, "message": "X-Plex-Client-Identifier is missing", "status": 400}]} "401": application/json: {"errors": [{"code": 1001, "message": "User could not be authenticated", "status": 401}, {"code": 1001, "message": "User could not be authenticated", "status": 401}, {"code": 1001, "message": "User could not be authenticated", "status": 401}]} + get-users: + "": + parameters: + header: + X-Plex-Client-Identifier: "3381b62b-9ab7-4e37-827b-203e9809eb58" + X-Plex-Product: "Plex for Roku" + X-Plex-Device: "Roku 3" + X-Plex-Device-Name: "Chrome" + X-Plex-Device-Screen-Resolution: "1487x1165,2560x1440" + X-Plex-Version: "2.4.1" + X-Plex-Platform: "Roku" + X-Plex-Features: "external-media,indirect-media,hub-style-list" + X-Plex-Model: "4200X" + X-Plex-Session-Id: "97e136ef-4ddd-4ff3-89a7-a5820c96c2ca" + X-Plex-Language: "en" + X-Plex-Platform-Version: "4.3 build 1057" + X-Plex-Token: "CV5xoxjTpFKUzBTShsaf" + responses: + "200": + application/xml: "0xa9CdebeF9E" + "400": + application/json: {"errors": [{"code": 1000, "message": "X-Plex-Client-Identifier is missing", "status": 400}, {"code": 1000, "message": "X-Plex-Client-Identifier is missing", "status": 400}]} + "401": + application/json: {"errors": [{"code": 1001, "message": "User could not be authenticated", "status": 401}, {"code": 1001, "message": "User could not be authenticated", "status": 401}]} examplesVersion: 1.0.0 generatedTests: {} diff --git a/.speakeasy/gen.yaml b/.speakeasy/gen.yaml index 3daea2c..87ed815 100644 --- a/.speakeasy/gen.yaml +++ b/.speakeasy/gen.yaml @@ -13,7 +13,7 @@ generation: oAuth2ClientCredentialsEnabled: true oAuth2PasswordEnabled: false python: - version: 0.21.2 + version: 0.22.0 additionalDependencies: dev: {} main: {} diff --git a/.speakeasy/workflow.lock b/.speakeasy/workflow.lock index 6a17263..c724328 100644 --- a/.speakeasy/workflow.lock +++ b/.speakeasy/workflow.lock @@ -1,4 +1,4 @@ -speakeasyVersion: 1.484.1 +speakeasyVersion: 1.487.0 sources: my-source: sourceNamespace: my-source @@ -8,19 +8,19 @@ sources: - latest plexapi: sourceNamespace: plexapi - sourceRevisionDigest: sha256:0b88c8bfc41def63e19e91fdd4d51bab07cb947cc3d39d2b44e8523a209cec10 - sourceBlobDigest: sha256:89d5b8427d4fb61b25751aebbaf71f9613958e9e91073fc084fed2c2ec62ce55 + sourceRevisionDigest: sha256:e6ab335dd91a4165f4510e4a3af8a55819c6310b7e66251266d2384376f3ccae + sourceBlobDigest: sha256:89d1b1133e400c89f0d06efe5e5168f5ba25a413e1ab8380d442d6b233e2e5a9 tags: - latest - - speakeasy-sdk-regen-1738800087 + - speakeasy-sdk-regen-1738886477 targets: plexpy: source: plexapi sourceNamespace: plexapi - sourceRevisionDigest: sha256:0b88c8bfc41def63e19e91fdd4d51bab07cb947cc3d39d2b44e8523a209cec10 - sourceBlobDigest: sha256:89d5b8427d4fb61b25751aebbaf71f9613958e9e91073fc084fed2c2ec62ce55 + sourceRevisionDigest: sha256:e6ab335dd91a4165f4510e4a3af8a55819c6310b7e66251266d2384376f3ccae + sourceBlobDigest: sha256:89d1b1133e400c89f0d06efe5e5168f5ba25a413e1ab8380d442d6b233e2e5a9 codeSamplesNamespace: code-samples-python-plexpy - codeSamplesRevisionDigest: sha256:e060abd1e6124c442333335008e39e994ede6ecf685a0d1e0e1c0813fc496ca5 + codeSamplesRevisionDigest: sha256:7c876b753cb5ce60e78319a51e8a0a436679e86c4e4a90d423d73532459dac53 workflow: workflowVersion: 1.0.0 speakeasyVersion: latest diff --git a/README.md b/README.md index 75a8d7e..ad180eb 100644 --- a/README.md +++ b/README.md @@ -299,6 +299,10 @@ asyncio.run(main()) * [check_for_updates](docs/sdks/updater/README.md#check_for_updates) - Checking for updates * [apply_updates](docs/sdks/updater/README.md#apply_updates) - Apply Updates +### [users](docs/sdks/users/README.md) + +* [get_users](docs/sdks/users/README.md#get_users) - Get list of all connected users + ### [video](docs/sdks/video/README.md) * [get_timeline](docs/sdks/video/README.md#get_timeline) - Get the timeline for a media item diff --git a/RELEASES.md b/RELEASES.md index b027cef..e947128 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -918,4 +918,14 @@ Based on: ### Generated - [python v0.21.2] . ### Releases -- [PyPI v0.21.2] https://pypi.org/project/plex-api-client/0.21.2 - . \ No newline at end of file +- [PyPI v0.21.2] https://pypi.org/project/plex-api-client/0.21.2 - . + +## 2025-02-07 00:01:02 +### Changes +Based on: +- OpenAPI Doc +- Speakeasy CLI 1.487.0 (2.506.0) https://github.com/speakeasy-api/speakeasy +### Generated +- [python v0.22.0] . +### Releases +- [PyPI v0.22.0] https://pypi.org/project/plex-api-client/0.22.0 - . \ No newline at end of file diff --git a/codeSamples.yaml b/codeSamples.yaml index ac58240..1319659 100644 --- a/codeSamples.yaml +++ b/codeSamples.yaml @@ -1470,6 +1470,36 @@ actions: # Handle response print(res.user_plex_account) + - target: $["paths"]["/users"]["get"] + update: + x-codeSamples: + - lang: python + label: users + source: |- + from plex_api_client import PlexAPI + + with PlexAPI() as plex_api: + + res = plex_api.users.get_users(request={ + "client_id": "3381b62b-9ab7-4e37-827b-203e9809eb58", + "client_name": "Plex for Roku", + "device_nickname": "Roku 3", + "device_name": "Chrome", + "device_screen_resolution": "1487x1165,2560x1440", + "client_version": "2.4.1", + "platform": "Roku", + "client_features": "external-media,indirect-media,hub-style-list", + "model": "4200X", + "x_plex_session_id": "97e136ef-4ddd-4ff3-89a7-a5820c96c2ca", + "x_plex_language": "en", + "platform_version": "4.3 build 1057", + "x_plex_token": "CV5xoxjTpFKUzBTShsaf", + }) + + assert res.body is not None + + # Handle response + print(res.body) - target: $["paths"]["/users/signin"]["post"] update: x-codeSamples: diff --git a/docs/models/errors/getusersbadrequest.md b/docs/models/errors/getusersbadrequest.md new file mode 100644 index 0000000..4c2958c --- /dev/null +++ b/docs/models/errors/getusersbadrequest.md @@ -0,0 +1,11 @@ +# GetUsersBadRequest + +Bad Request - A parameter was not specified, or was specified incorrectly. + + +## Fields + +| Field | Type | Required | Description | +| -------------------------------------------------------------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------- | +| `errors` | List[[errors.GetUsersErrors](../../models/errors/getuserserrors.md)] | :heavy_minus_sign: | N/A | +| `raw_response` | [httpx.Response](https://www.python-httpx.org/api/#response) | :heavy_minus_sign: | Raw HTTP response; suitable for custom response parsing | \ No newline at end of file diff --git a/docs/models/errors/getuserserrors.md b/docs/models/errors/getuserserrors.md new file mode 100644 index 0000000..f397fb3 --- /dev/null +++ b/docs/models/errors/getuserserrors.md @@ -0,0 +1,10 @@ +# GetUsersErrors + + +## Fields + +| Field | Type | Required | Description | Example | +| ----------------------------------- | ----------------------------------- | ----------------------------------- | ----------------------------------- | ----------------------------------- | +| `code` | *Optional[int]* | :heavy_minus_sign: | N/A | 1000 | +| `message` | *Optional[str]* | :heavy_minus_sign: | N/A | X-Plex-Client-Identifier is missing | +| `status` | *Optional[int]* | :heavy_minus_sign: | N/A | 400 | \ No newline at end of file diff --git a/docs/models/errors/getusersunauthorized.md b/docs/models/errors/getusersunauthorized.md new file mode 100644 index 0000000..cb96a71 --- /dev/null +++ b/docs/models/errors/getusersunauthorized.md @@ -0,0 +1,11 @@ +# GetUsersUnauthorized + +Unauthorized - Returned if the X-Plex-Token is missing from the header or query. + + +## Fields + +| Field | Type | Required | Description | +| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | +| `errors` | List[[errors.GetUsersUsersErrors](../../models/errors/getusersuserserrors.md)] | :heavy_minus_sign: | N/A | +| `raw_response` | [httpx.Response](https://www.python-httpx.org/api/#response) | :heavy_minus_sign: | Raw HTTP response; suitable for custom response parsing | \ No newline at end of file diff --git a/docs/models/errors/getusersuserserrors.md b/docs/models/errors/getusersuserserrors.md new file mode 100644 index 0000000..5f66d20 --- /dev/null +++ b/docs/models/errors/getusersuserserrors.md @@ -0,0 +1,10 @@ +# GetUsersUsersErrors + + +## Fields + +| Field | Type | Required | Description | Example | +| ------------------------------- | ------------------------------- | ------------------------------- | ------------------------------- | ------------------------------- | +| `code` | *Optional[int]* | :heavy_minus_sign: | N/A | 1001 | +| `message` | *Optional[str]* | :heavy_minus_sign: | N/A | User could not be authenticated | +| `status` | *Optional[int]* | :heavy_minus_sign: | N/A | 401 | \ No newline at end of file diff --git a/docs/models/operations/alllibraries.md b/docs/models/operations/alllibraries.md new file mode 100644 index 0000000..31424c0 --- /dev/null +++ b/docs/models/operations/alllibraries.md @@ -0,0 +1,11 @@ +# AllLibraries + +Indicates if the user has access to all libraries. + + +## Values + +| Name | Value | +| --------- | --------- | +| `DISABLE` | 0 | +| `ENABLE` | 1 | \ No newline at end of file diff --git a/docs/models/operations/allowcameraupload.md b/docs/models/operations/allowcameraupload.md new file mode 100644 index 0000000..5baa5ec --- /dev/null +++ b/docs/models/operations/allowcameraupload.md @@ -0,0 +1,11 @@ +# AllowCameraUpload + +Indicates if the user is allowed to upload from a camera. + + +## Values + +| Name | Value | +| --------- | --------- | +| `DISABLE` | 0 | +| `ENABLE` | 1 | \ No newline at end of file diff --git a/docs/models/operations/allowchannels.md b/docs/models/operations/allowchannels.md new file mode 100644 index 0000000..1e9ef0e --- /dev/null +++ b/docs/models/operations/allowchannels.md @@ -0,0 +1,11 @@ +# AllowChannels + +Indicates if the user has access to channels. + + +## Values + +| Name | Value | +| --------- | --------- | +| `DISABLE` | 0 | +| `ENABLE` | 1 | \ No newline at end of file diff --git a/docs/models/operations/allowsubtitleadmin.md b/docs/models/operations/allowsubtitleadmin.md new file mode 100644 index 0000000..2078c0e --- /dev/null +++ b/docs/models/operations/allowsubtitleadmin.md @@ -0,0 +1,11 @@ +# AllowSubtitleAdmin + +Indicates if the user can manage subtitles. + + +## Values + +| Name | Value | +| --------- | --------- | +| `DISABLE` | 0 | +| `ENABLE` | 1 | \ No newline at end of file diff --git a/docs/models/operations/allowsync.md b/docs/models/operations/allowsync.md new file mode 100644 index 0000000..82d8fd4 --- /dev/null +++ b/docs/models/operations/allowsync.md @@ -0,0 +1,11 @@ +# AllowSync + +Indicates if the user is allowed to sync media. + + +## Values + +| Name | Value | +| --------- | --------- | +| `DISABLE` | 0 | +| `ENABLE` | 1 | \ No newline at end of file diff --git a/docs/models/operations/allowtuners.md b/docs/models/operations/allowtuners.md new file mode 100644 index 0000000..d0edbab --- /dev/null +++ b/docs/models/operations/allowtuners.md @@ -0,0 +1,11 @@ +# AllowTuners + +Indicates if the user is allowed to use tuners. + + +## Values + +| Name | Value | +| --------- | --------- | +| `DISABLE` | 0 | +| `ENABLE` | 1 | \ No newline at end of file diff --git a/docs/models/operations/gettopwatchedcontentmetadata.md b/docs/models/operations/gettopwatchedcontentmetadata.md index 692e725..c10f587 100644 --- a/docs/models/operations/gettopwatchedcontentmetadata.md +++ b/docs/models/operations/gettopwatchedcontentmetadata.md @@ -36,4 +36,4 @@ | `country` | List[[operations.GetTopWatchedContentCountry](../../models/operations/gettopwatchedcontentcountry.md)] | :heavy_minus_sign: | N/A | | | `guids` | List[[operations.GetTopWatchedContentGuids](../../models/operations/gettopwatchedcontentguids.md)] | :heavy_minus_sign: | N/A | | | `role` | List[[operations.GetTopWatchedContentRole](../../models/operations/gettopwatchedcontentrole.md)] | :heavy_minus_sign: | N/A | | -| `user` | List[[operations.User](../../models/operations/user.md)] | :heavy_minus_sign: | N/A | | \ No newline at end of file +| `user` | List[[operations.GetTopWatchedContentUser](../../models/operations/gettopwatchedcontentuser.md)] | :heavy_minus_sign: | N/A | | \ No newline at end of file diff --git a/docs/models/operations/gettopwatchedcontentuser.md b/docs/models/operations/gettopwatchedcontentuser.md new file mode 100644 index 0000000..afc40e0 --- /dev/null +++ b/docs/models/operations/gettopwatchedcontentuser.md @@ -0,0 +1,8 @@ +# GetTopWatchedContentUser + + +## Fields + +| Field | Type | Required | Description | Example | +| ------------------ | ------------------ | ------------------ | ------------------ | ------------------ | +| `id` | *Optional[int]* | :heavy_minus_sign: | N/A | 220 | \ No newline at end of file diff --git a/docs/models/operations/getusersmediacontainer.md b/docs/models/operations/getusersmediacontainer.md new file mode 100644 index 0000000..aed8301 --- /dev/null +++ b/docs/models/operations/getusersmediacontainer.md @@ -0,0 +1,15 @@ +# GetUsersMediaContainer + +Container holding user and server details. + + +## Fields + +| Field | Type | Required | Description | Example | +| -------------------------------------------------------- | -------------------------------------------------------- | -------------------------------------------------------- | -------------------------------------------------------- | -------------------------------------------------------- | +| `friendly_name` | *str* | :heavy_check_mark: | The friendly name of the Plex instance. | myPlex | +| `identifier` | *str* | :heavy_check_mark: | N/A | com.plexapp.plugins.myplex | +| `machine_identifier` | *str* | :heavy_check_mark: | Unique Machine identifier of the Plex server. | 3dff4c4da3b1229a649aa574a9e2b419a684a20e | +| `total_size` | *int* | :heavy_check_mark: | Total number of users. | 30 | +| `size` | *int* | :heavy_check_mark: | Number of users in the current response. | 30 | +| `user` | List[[operations.User](../../models/operations/user.md)] | :heavy_check_mark: | List of users with access to the Plex server. | | \ No newline at end of file diff --git a/docs/models/operations/getusersrequest.md b/docs/models/operations/getusersrequest.md new file mode 100644 index 0000000..0082cfa --- /dev/null +++ b/docs/models/operations/getusersrequest.md @@ -0,0 +1,20 @@ +# GetUsersRequest + + +## Fields + +| Field | Type | Required | Description | Example | +| ---------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| `client_id` | *str* | :heavy_check_mark: | An opaque identifier unique to the client (UUID, serial number, or other unique device ID) | 3381b62b-9ab7-4e37-827b-203e9809eb58 | +| `x_plex_token` | *str* | :heavy_check_mark: | An authentication token, obtained from plex.tv | CV5xoxjTpFKUzBTShsaf | +| `client_name` | *Optional[str]* | :heavy_minus_sign: | The name of the client application. (Plex Web, Plex Media Server, etc.) | Plex for Roku | +| `device_nickname` | *Optional[str]* | :heavy_minus_sign: | A relatively friendly name for the client device | Roku 3 | +| `device_name` | *Optional[str]* | :heavy_minus_sign: | The name of the device the client application is running on. This is used to track the client application and its usage. (Chrome, Safari, etc.) | Chrome | +| `device_screen_resolution` | *Optional[str]* | :heavy_minus_sign: | The resolution of the device the client application is running on. This is used to track the client application and its usage. (1487x1165,2560x1440) | 1487x1165,2560x1440 | +| `client_version` | *Optional[str]* | :heavy_minus_sign: | The version of the client application. | 2.4.1 | +| `platform` | *Optional[str]* | :heavy_minus_sign: | The platform of the client application. | Roku | +| `client_features` | *Optional[str]* | :heavy_minus_sign: | The features of the client application. This is used to track the client application and its usage. (external-media,indirect-media,hub-style-list) | external-media,indirect-media,hub-style-list | +| `model` | *Optional[str]* | :heavy_minus_sign: | A potentially less friendly identifier for the device model | 4200X | +| `x_plex_session_id` | *Optional[str]* | :heavy_minus_sign: | The session ID of the client application. This is used to track the client application and its usage. (97e136ef-4ddd-4ff3-89a7-a5820c96c2ca) | 97e136ef-4ddd-4ff3-89a7-a5820c96c2ca | +| `x_plex_language` | *Optional[str]* | :heavy_minus_sign: | The language of the client application. | en | +| `platform_version` | *Optional[str]* | :heavy_minus_sign: | The version of the platform | 4.3 build 1057 | \ No newline at end of file diff --git a/docs/models/operations/getusersresponse.md b/docs/models/operations/getusersresponse.md new file mode 100644 index 0000000..18c2af5 --- /dev/null +++ b/docs/models/operations/getusersresponse.md @@ -0,0 +1,11 @@ +# GetUsersResponse + + +## Fields + +| Field | Type | Required | Description | +| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | +| `content_type` | *str* | :heavy_check_mark: | HTTP response content type for this operation | +| `status_code` | *int* | :heavy_check_mark: | HTTP response status code for this operation | +| `raw_response` | [httpx.Response](https://www.python-httpx.org/api/#response) | :heavy_check_mark: | Raw HTTP response; suitable for custom response parsing | +| `body` | *Optional[bytes]* | :heavy_minus_sign: | N/A | \ No newline at end of file diff --git a/docs/models/operations/getusersresponsebody.md b/docs/models/operations/getusersresponsebody.md new file mode 100644 index 0000000..7acd1e7 --- /dev/null +++ b/docs/models/operations/getusersresponsebody.md @@ -0,0 +1,10 @@ +# GetUsersResponseBody + +Successful response with media container data in XML + + +## Fields + +| Field | Type | Required | Description | +| ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------ | +| `media_container` | [Optional[operations.GetUsersMediaContainer]](../../models/operations/getusersmediacontainer.md) | :heavy_minus_sign: | Container holding user and server details. | \ No newline at end of file diff --git a/docs/models/operations/getusersserver.md b/docs/models/operations/getusersserver.md new file mode 100644 index 0000000..edb1333 --- /dev/null +++ b/docs/models/operations/getusersserver.md @@ -0,0 +1,16 @@ +# GetUsersServer + + +## Fields + +| Field | Type | Required | Description | Example | +| ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | +| `id` | *int* | :heavy_check_mark: | Unique ID of the server of the connected user | 907759180 | +| `server_id` | *int* | :heavy_check_mark: | ID of the actual Plex server. | 9999999 | +| `machine_identifier` | *str* | :heavy_check_mark: | Machine identifier of the Plex server. | fbb8aa6be6e0c997c6268bc2b4431c8807f70a3 | +| `name` | *str* | :heavy_check_mark: | Name of the Plex server of the connected user. | ConnectedUserFlix | +| `last_seen_at` | *int* | :heavy_check_mark: | N/A | 1556281940 | +| `num_libraries` | *int* | :heavy_check_mark: | Number of libraries in the server this user has access to. | 16 | +| `all_libraries` | [Optional[operations.AllLibraries]](../../models/operations/alllibraries.md) | :heavy_minus_sign: | N/A | 1 | +| `owned` | [Optional[operations.Owned]](../../models/operations/owned.md) | :heavy_minus_sign: | N/A | 1 | +| `pending` | [Optional[operations.Pending]](../../models/operations/pending.md) | :heavy_minus_sign: | N/A | 1 | \ No newline at end of file diff --git a/docs/models/operations/home.md b/docs/models/operations/home.md new file mode 100644 index 0000000..b7b9973 --- /dev/null +++ b/docs/models/operations/home.md @@ -0,0 +1,11 @@ +# Home + +Indicates if the user is part of a home group. + + +## Values + +| Name | Value | +| --------- | --------- | +| `DISABLE` | 0 | +| `ENABLE` | 1 | \ No newline at end of file diff --git a/docs/models/operations/owned.md b/docs/models/operations/owned.md new file mode 100644 index 0000000..c1d272a --- /dev/null +++ b/docs/models/operations/owned.md @@ -0,0 +1,11 @@ +# Owned + +Indicates if the user owns the server. + + +## Values + +| Name | Value | +| --------- | --------- | +| `DISABLE` | 0 | +| `ENABLE` | 1 | \ No newline at end of file diff --git a/docs/models/operations/pending.md b/docs/models/operations/pending.md new file mode 100644 index 0000000..9596ebe --- /dev/null +++ b/docs/models/operations/pending.md @@ -0,0 +1,11 @@ +# Pending + +Indicates if the server is pending approval. + + +## Values + +| Name | Value | +| --------- | --------- | +| `DISABLE` | 0 | +| `ENABLE` | 1 | \ No newline at end of file diff --git a/docs/models/operations/protected.md b/docs/models/operations/protected.md new file mode 100644 index 0000000..b7f1a5f --- /dev/null +++ b/docs/models/operations/protected.md @@ -0,0 +1,11 @@ +# Protected + +Indicates whether the account is protected. + + +## Values + +| Name | Value | +| --------- | --------- | +| `DISABLE` | 0 | +| `ENABLE` | 1 | \ No newline at end of file diff --git a/docs/models/operations/restricted.md b/docs/models/operations/restricted.md new file mode 100644 index 0000000..983ac01 --- /dev/null +++ b/docs/models/operations/restricted.md @@ -0,0 +1,11 @@ +# Restricted + +Indicates if the user has restricted access. + + +## Values + +| Name | Value | +| --------- | --------- | +| `DISABLE` | 0 | +| `ENABLE` | 1 | \ No newline at end of file diff --git a/docs/models/operations/user.md b/docs/models/operations/user.md index 8ad5b79..46cd451 100644 --- a/docs/models/operations/user.md +++ b/docs/models/operations/user.md @@ -3,6 +3,25 @@ ## Fields -| Field | Type | Required | Description | Example | -| ------------------ | ------------------ | ------------------ | ------------------ | ------------------ | -| `id` | *Optional[int]* | :heavy_minus_sign: | N/A | 220 | \ No newline at end of file +| Field | Type | Required | Description | Example | +| ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | +| `id` | *int* | :heavy_check_mark: | User's unique ID. | 22526914 | +| `title` | *str* | :heavy_check_mark: | User's display name. | Plex User | +| `username` | *str* | :heavy_check_mark: | User's username. | zgfuc7krcqfimrmb9lsl5j | +| `email` | *str* | :heavy_check_mark: | User's email address. | zgfuc7krcqfimrmb9lsl5j@protonmail.com | +| `thumb` | *str* | :heavy_check_mark: | URL to the user's avatar image. | https://plex.tv/users/3346028014e93acd/avatar?c=1731605021 | +| `server` | List[[operations.GetUsersServer](../../models/operations/getusersserver.md)] | :heavy_check_mark: | List of servers owned by the user. | | +| `recommendations_playlist_id` | *OptionalNullable[str]* | :heavy_minus_sign: | ID of the user's recommendation playlist. | | +| `protected` | [Optional[operations.Protected]](../../models/operations/protected.md) | :heavy_minus_sign: | N/A | 1 | +| `home` | [Optional[operations.Home]](../../models/operations/home.md) | :heavy_minus_sign: | N/A | 1 | +| `allow_tuners` | [Optional[operations.AllowTuners]](../../models/operations/allowtuners.md) | :heavy_minus_sign: | N/A | 1 | +| `allow_sync` | [Optional[operations.AllowSync]](../../models/operations/allowsync.md) | :heavy_minus_sign: | N/A | 1 | +| `allow_camera_upload` | [Optional[operations.AllowCameraUpload]](../../models/operations/allowcameraupload.md) | :heavy_minus_sign: | N/A | 1 | +| `allow_channels` | [Optional[operations.AllowChannels]](../../models/operations/allowchannels.md) | :heavy_minus_sign: | N/A | 1 | +| `allow_subtitle_admin` | [Optional[operations.AllowSubtitleAdmin]](../../models/operations/allowsubtitleadmin.md) | :heavy_minus_sign: | N/A | 1 | +| `filter_all` | *OptionalNullable[str]* | :heavy_minus_sign: | Filters applied for all content. | | +| `filter_movies` | *OptionalNullable[str]* | :heavy_minus_sign: | Filters applied for movies. | | +| `filter_music` | *OptionalNullable[str]* | :heavy_minus_sign: | Filters applied for music. | | +| `filter_photos` | *OptionalNullable[str]* | :heavy_minus_sign: | Filters applied for photos. | | +| `filter_television` | *Optional[str]* | :heavy_minus_sign: | Filters applied for television. | | +| `restricted` | [Optional[operations.Restricted]](../../models/operations/restricted.md) | :heavy_minus_sign: | N/A | 1 | \ No newline at end of file diff --git a/docs/sdks/users/README.md b/docs/sdks/users/README.md new file mode 100644 index 0000000..44b0e63 --- /dev/null +++ b/docs/sdks/users/README.md @@ -0,0 +1,62 @@ +# Users +(*users*) + +## Overview + +### Available Operations + +* [get_users](#get_users) - Get list of all connected users + +## get_users + +Get list of all users that are friends and have library access with the provided Plex authentication token + +### Example Usage + +```python +from plex_api_client import PlexAPI + +with PlexAPI() as plex_api: + + res = plex_api.users.get_users(request={ + "client_id": "3381b62b-9ab7-4e37-827b-203e9809eb58", + "x_plex_token": "CV5xoxjTpFKUzBTShsaf", + "client_name": "Plex for Roku", + "device_nickname": "Roku 3", + "device_name": "Chrome", + "device_screen_resolution": "1487x1165,2560x1440", + "client_version": "2.4.1", + "platform": "Roku", + "client_features": "external-media,indirect-media,hub-style-list", + "model": "4200X", + "x_plex_session_id": "97e136ef-4ddd-4ff3-89a7-a5820c96c2ca", + "x_plex_language": "en", + "platform_version": "4.3 build 1057", + }) + + assert res.body is not None + + # Handle response + print(res.body) + +``` + +### Parameters + +| Parameter | Type | Required | Description | +| ------------------------------------------------------------------------ | ------------------------------------------------------------------------ | ------------------------------------------------------------------------ | ------------------------------------------------------------------------ | +| `request` | [operations.GetUsersRequest](../../models/operations/getusersrequest.md) | :heavy_check_mark: | The request object to use for the request. | +| `retries` | [Optional[utils.RetryConfig]](../../models/utils/retryconfig.md) | :heavy_minus_sign: | Configuration to override the default retry behavior of the client. | +| `server_url` | *Optional[str]* | :heavy_minus_sign: | An optional server URL to use. | + +### Response + +**[operations.GetUsersResponse](../../models/operations/getusersresponse.md)** + +### Errors + +| Error Type | Status Code | Content Type | +| --------------------------- | --------------------------- | --------------------------- | +| errors.GetUsersBadRequest | 400 | application/json | +| errors.GetUsersUnauthorized | 401 | application/json | +| errors.SDKError | 4XX, 5XX | \*/\* | \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 5cd6c47..845bdf9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "plex-api-client" -version = "0.21.2" +version = "0.22.0" description = "Python Client SDK Generated by Speakeasy" authors = [{ name = "Speakeasy" },] readme = "README-PYPI.md" diff --git a/src/plex_api_client/_version.py b/src/plex_api_client/_version.py index 62a2129..f5b790a 100644 --- a/src/plex_api_client/_version.py +++ b/src/plex_api_client/_version.py @@ -3,10 +3,10 @@ import importlib.metadata __title__: str = "plex-api-client" -__version__: str = "0.21.2" +__version__: str = "0.22.0" __openapi_doc_version__: str = "0.0.3" -__gen_version__: str = "2.503.2" -__user_agent__: str = "speakeasy-sdk/python 0.21.2 2.503.2 0.0.3 plex-api-client" +__gen_version__: str = "2.506.0" +__user_agent__: str = "speakeasy-sdk/python 0.22.0 2.506.0 0.0.3 plex-api-client" try: if __package__ is not None: diff --git a/src/plex_api_client/models/errors/__init__.py b/src/plex_api_client/models/errors/__init__.py index 9c02d7e..3331724 100644 --- a/src/plex_api_client/models/errors/__init__.py +++ b/src/plex_api_client/models/errors/__init__.py @@ -234,6 +234,16 @@ from .get_thumb_image import ( GetThumbImageUnauthorized, GetThumbImageUnauthorizedData, ) +from .get_users import ( + GetUsersBadRequest, + GetUsersBadRequestData, + GetUsersErrors, + GetUsersErrorsTypedDict, + GetUsersUnauthorized, + GetUsersUnauthorizedData, + GetUsersUsersErrors, + GetUsersUsersErrorsTypedDict, +) from .get_watch_list import ( GetWatchListBadRequest, GetWatchListBadRequestData, @@ -1234,6 +1244,14 @@ __all__ = [ "GetUserFriendsPlexErrorsTypedDict", "GetUserFriendsUnauthorized", "GetUserFriendsUnauthorizedData", + "GetUsersBadRequest", + "GetUsersBadRequestData", + "GetUsersErrors", + "GetUsersErrorsTypedDict", + "GetUsersUnauthorized", + "GetUsersUnauthorizedData", + "GetUsersUsersErrors", + "GetUsersUsersErrorsTypedDict", "GetWatchListBadRequest", "GetWatchListBadRequestData", "GetWatchListErrors", diff --git a/src/plex_api_client/models/errors/get_users.py b/src/plex_api_client/models/errors/get_users.py new file mode 100644 index 0000000..f964cdf --- /dev/null +++ b/src/plex_api_client/models/errors/get_users.py @@ -0,0 +1,79 @@ +"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" + +from __future__ import annotations +import httpx +from plex_api_client import utils +from plex_api_client.types import BaseModel +import pydantic +from typing import List, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class GetUsersUsersErrorsTypedDict(TypedDict): + code: NotRequired[int] + message: NotRequired[str] + status: NotRequired[int] + + +class GetUsersUsersErrors(BaseModel): + code: Optional[int] = None + + message: Optional[str] = None + + status: Optional[int] = None + + +class GetUsersUnauthorizedData(BaseModel): + errors: Optional[List[GetUsersUsersErrors]] = None + + raw_response: Annotated[Optional[httpx.Response], pydantic.Field(exclude=True)] = ( + None + ) + r"""Raw HTTP response; suitable for custom response parsing""" + + +class GetUsersUnauthorized(Exception): + r"""Unauthorized - Returned if the X-Plex-Token is missing from the header or query.""" + + data: GetUsersUnauthorizedData + + def __init__(self, data: GetUsersUnauthorizedData): + self.data = data + + def __str__(self) -> str: + return utils.marshal_json(self.data, GetUsersUnauthorizedData) + + +class GetUsersErrorsTypedDict(TypedDict): + code: NotRequired[int] + message: NotRequired[str] + status: NotRequired[int] + + +class GetUsersErrors(BaseModel): + code: Optional[int] = None + + message: Optional[str] = None + + status: Optional[int] = None + + +class GetUsersBadRequestData(BaseModel): + errors: Optional[List[GetUsersErrors]] = None + + raw_response: Annotated[Optional[httpx.Response], pydantic.Field(exclude=True)] = ( + None + ) + r"""Raw HTTP response; suitable for custom response parsing""" + + +class GetUsersBadRequest(Exception): + r"""Bad Request - A parameter was not specified, or was specified incorrectly.""" + + data: GetUsersBadRequestData + + def __init__(self, data: GetUsersBadRequestData): + self.data = data + + def __str__(self) -> str: + return utils.marshal_json(self.data, GetUsersBadRequestData) diff --git a/src/plex_api_client/models/operations/__init__.py b/src/plex_api_client/models/operations/__init__.py index 0f0ebaf..59922cc 100644 --- a/src/plex_api_client/models/operations/__init__.py +++ b/src/plex_api_client/models/operations/__init__.py @@ -482,6 +482,32 @@ from .get_thumb_image import ( GetThumbImageResponse, GetThumbImageResponseTypedDict, ) +from .get_users import ( + AllLibraries, + AllowCameraUpload, + AllowChannels, + AllowSubtitleAdmin, + AllowSync, + AllowTuners, + GET_USERS_SERVERS, + GetUsersMediaContainer, + GetUsersMediaContainerTypedDict, + GetUsersRequest, + GetUsersRequestTypedDict, + GetUsersResponse, + GetUsersResponseBody, + GetUsersResponseBodyTypedDict, + GetUsersResponseTypedDict, + GetUsersServer, + GetUsersServerTypedDict, + Home, + Owned, + Pending, + Protected, + Restricted, + User, + UserTypedDict, +) from .get_watch_list import ( Filter, GET_WATCH_LIST_SERVERS, @@ -929,8 +955,8 @@ from .gettopwatchedcontent import ( GetTopWatchedContentResponseTypedDict, GetTopWatchedContentRole, GetTopWatchedContentRoleTypedDict, - User, - UserTypedDict, + GetTopWatchedContentUser, + GetTopWatchedContentUserTypedDict, ) from .gettranscodesessions import ( GetTranscodeSessionsMediaContainer, @@ -1108,6 +1134,12 @@ __all__ = [ "AddPlaylistContentsResponseBody", "AddPlaylistContentsResponseBodyTypedDict", "AddPlaylistContentsResponseTypedDict", + "AllLibraries", + "AllowCameraUpload", + "AllowChannels", + "AllowSubtitleAdmin", + "AllowSync", + "AllowTuners", "ApplyUpdatesRequest", "ApplyUpdatesRequestTypedDict", "ApplyUpdatesResponse", @@ -1189,6 +1221,7 @@ __all__ = [ "GET_SERVER_RESOURCES_SERVERS", "GET_TOKEN_BY_PIN_ID_SERVERS", "GET_TOKEN_DETAILS_SERVERS", + "GET_USERS_SERVERS", "GET_USER_FRIENDS_SERVERS", "GET_WATCH_LIST_SERVERS", "Genre", @@ -1797,6 +1830,8 @@ __all__ = [ "GetTopWatchedContentResponseTypedDict", "GetTopWatchedContentRole", "GetTopWatchedContentRoleTypedDict", + "GetTopWatchedContentUser", + "GetTopWatchedContentUserTypedDict", "GetTranscodeSessionsMediaContainer", "GetTranscodeSessionsMediaContainerTypedDict", "GetTranscodeSessionsResponse", @@ -1816,6 +1851,16 @@ __all__ = [ "GetUpdateStatusResponseTypedDict", "GetUserFriendsResponse", "GetUserFriendsResponseTypedDict", + "GetUsersMediaContainer", + "GetUsersMediaContainerTypedDict", + "GetUsersRequest", + "GetUsersRequestTypedDict", + "GetUsersResponse", + "GetUsersResponseBody", + "GetUsersResponseBodyTypedDict", + "GetUsersResponseTypedDict", + "GetUsersServer", + "GetUsersServerTypedDict", "GetWatchListRequest", "GetWatchListRequestTypedDict", "GetWatchListResponse", @@ -1825,6 +1870,7 @@ __all__ = [ "Guids", "GuidsTypedDict", "HasThumbnail", + "Home", "Hub", "HubTypedDict", "Image", @@ -1880,12 +1926,14 @@ __all__ = [ "Operator", "OperatorTypedDict", "OptimizedForStreaming", + "Owned", "POST_USERS_SIGN_IN_DATA_SERVERS", "Part", "PartTypedDict", "PastSubscription", "PastSubscriptionTypedDict", "PathParamTaskName", + "Pending", "PerformSearchRequest", "PerformSearchRequestTypedDict", "PerformSearchResponse", @@ -1929,6 +1977,7 @@ __all__ = [ "PostUsersSignInDataWatchedIndicator", "Producer", "ProducerTypedDict", + "Protected", "Protocol", "Provider", "ProviderTypedDict", @@ -1947,6 +1996,7 @@ __all__ = [ "ReleaseTypedDict", "ResponseBody", "ResponseBodyTypedDict", + "Restricted", "Role", "RoleTypedDict", "Scope", diff --git a/src/plex_api_client/models/operations/get_users.py b/src/plex_api_client/models/operations/get_users.py new file mode 100644 index 0000000..258e6dd --- /dev/null +++ b/src/plex_api_client/models/operations/get_users.py @@ -0,0 +1,476 @@ +"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" + +from __future__ import annotations +from enum import Enum +import httpx +from plex_api_client.types import ( + BaseModel, + Nullable, + OptionalNullable, + UNSET, + UNSET_SENTINEL, +) +from plex_api_client.utils import FieldMetadata, HeaderMetadata +import pydantic +from pydantic import model_serializer +from typing import List, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + +GET_USERS_SERVERS = [ + "https://plex.tv/api", +] + + +class GetUsersRequestTypedDict(TypedDict): + client_id: str + r"""An opaque identifier unique to the client (UUID, serial number, or other unique device ID)""" + x_plex_token: str + r"""An authentication token, obtained from plex.tv""" + client_name: NotRequired[str] + r"""The name of the client application. (Plex Web, Plex Media Server, etc.)""" + device_nickname: NotRequired[str] + r"""A relatively friendly name for the client device""" + device_name: NotRequired[str] + r"""The name of the device the client application is running on. This is used to track the client application and its usage. (Chrome, Safari, etc.)""" + device_screen_resolution: NotRequired[str] + r"""The resolution of the device the client application is running on. This is used to track the client application and its usage. (1487x1165,2560x1440)""" + client_version: NotRequired[str] + r"""The version of the client application.""" + platform: NotRequired[str] + r"""The platform of the client application.""" + client_features: NotRequired[str] + r"""The features of the client application. This is used to track the client application and its usage. (external-media,indirect-media,hub-style-list)""" + model: NotRequired[str] + r"""A potentially less friendly identifier for the device model""" + x_plex_session_id: NotRequired[str] + r"""The session ID of the client application. This is used to track the client application and its usage. (97e136ef-4ddd-4ff3-89a7-a5820c96c2ca)""" + x_plex_language: NotRequired[str] + r"""The language of the client application.""" + platform_version: NotRequired[str] + r"""The version of the platform""" + + +class GetUsersRequest(BaseModel): + client_id: Annotated[ + str, + pydantic.Field(alias="X-Plex-Client-Identifier"), + FieldMetadata(header=HeaderMetadata(style="simple", explode=False)), + ] + r"""An opaque identifier unique to the client (UUID, serial number, or other unique device ID)""" + + x_plex_token: Annotated[ + str, + pydantic.Field(alias="X-Plex-Token"), + FieldMetadata(header=HeaderMetadata(style="simple", explode=False)), + ] + r"""An authentication token, obtained from plex.tv""" + + client_name: Annotated[ + Optional[str], + pydantic.Field(alias="X-Plex-Product"), + FieldMetadata(header=HeaderMetadata(style="simple", explode=False)), + ] = None + r"""The name of the client application. (Plex Web, Plex Media Server, etc.)""" + + device_nickname: Annotated[ + Optional[str], + pydantic.Field(alias="X-Plex-Device"), + FieldMetadata(header=HeaderMetadata(style="simple", explode=False)), + ] = None + r"""A relatively friendly name for the client device""" + + device_name: Annotated[ + Optional[str], + pydantic.Field(alias="X-Plex-Device-Name"), + FieldMetadata(header=HeaderMetadata(style="simple", explode=False)), + ] = None + r"""The name of the device the client application is running on. This is used to track the client application and its usage. (Chrome, Safari, etc.)""" + + device_screen_resolution: Annotated[ + Optional[str], + pydantic.Field(alias="X-Plex-Device-Screen-Resolution"), + FieldMetadata(header=HeaderMetadata(style="simple", explode=False)), + ] = None + r"""The resolution of the device the client application is running on. This is used to track the client application and its usage. (1487x1165,2560x1440)""" + + client_version: Annotated[ + Optional[str], + pydantic.Field(alias="X-Plex-Version"), + FieldMetadata(header=HeaderMetadata(style="simple", explode=False)), + ] = None + r"""The version of the client application.""" + + platform: Annotated[ + Optional[str], + pydantic.Field(alias="X-Plex-Platform"), + FieldMetadata(header=HeaderMetadata(style="simple", explode=False)), + ] = None + r"""The platform of the client application.""" + + client_features: Annotated[ + Optional[str], + pydantic.Field(alias="X-Plex-Features"), + FieldMetadata(header=HeaderMetadata(style="simple", explode=False)), + ] = None + r"""The features of the client application. This is used to track the client application and its usage. (external-media,indirect-media,hub-style-list)""" + + model: Annotated[ + Optional[str], + pydantic.Field(alias="X-Plex-Model"), + FieldMetadata(header=HeaderMetadata(style="simple", explode=False)), + ] = None + r"""A potentially less friendly identifier for the device model""" + + x_plex_session_id: Annotated[ + Optional[str], + pydantic.Field(alias="X-Plex-Session-Id"), + FieldMetadata(header=HeaderMetadata(style="simple", explode=False)), + ] = None + r"""The session ID of the client application. This is used to track the client application and its usage. (97e136ef-4ddd-4ff3-89a7-a5820c96c2ca)""" + + x_plex_language: Annotated[ + Optional[str], + pydantic.Field(alias="X-Plex-Language"), + FieldMetadata(header=HeaderMetadata(style="simple", explode=False)), + ] = None + r"""The language of the client application.""" + + platform_version: Annotated[ + Optional[str], + pydantic.Field(alias="X-Plex-Platform-Version"), + FieldMetadata(header=HeaderMetadata(style="simple", explode=False)), + ] = None + r"""The version of the platform""" + + +class Protected(int, Enum): + r"""Indicates whether the account is protected.""" + + DISABLE = 0 + ENABLE = 1 + + +class Home(int, Enum): + r"""Indicates if the user is part of a home group.""" + + DISABLE = 0 + ENABLE = 1 + + +class AllowTuners(int, Enum): + r"""Indicates if the user is allowed to use tuners.""" + + DISABLE = 0 + ENABLE = 1 + + +class AllowSync(int, Enum): + r"""Indicates if the user is allowed to sync media.""" + + DISABLE = 0 + ENABLE = 1 + + +class AllowCameraUpload(int, Enum): + r"""Indicates if the user is allowed to upload from a camera.""" + + DISABLE = 0 + ENABLE = 1 + + +class AllowChannels(int, Enum): + r"""Indicates if the user has access to channels.""" + + DISABLE = 0 + ENABLE = 1 + + +class AllowSubtitleAdmin(int, Enum): + r"""Indicates if the user can manage subtitles.""" + + DISABLE = 0 + ENABLE = 1 + + +class Restricted(int, Enum): + r"""Indicates if the user has restricted access.""" + + DISABLE = 0 + ENABLE = 1 + + +class AllLibraries(int, Enum): + r"""Indicates if the user has access to all libraries.""" + + DISABLE = 0 + ENABLE = 1 + + +class Owned(int, Enum): + r"""Indicates if the user owns the server.""" + + DISABLE = 0 + ENABLE = 1 + + +class Pending(int, Enum): + r"""Indicates if the server is pending approval.""" + + DISABLE = 0 + ENABLE = 1 + + +class GetUsersServerTypedDict(TypedDict): + id: int + r"""Unique ID of the server of the connected user""" + server_id: int + r"""ID of the actual Plex server.""" + machine_identifier: str + r"""Machine identifier of the Plex server.""" + name: str + r"""Name of the Plex server of the connected user.""" + last_seen_at: int + num_libraries: int + r"""Number of libraries in the server this user has access to.""" + all_libraries: NotRequired[AllLibraries] + owned: NotRequired[Owned] + pending: NotRequired[Pending] + + +class GetUsersServer(BaseModel): + id: int + r"""Unique ID of the server of the connected user""" + + server_id: int + r"""ID of the actual Plex server.""" + + machine_identifier: str + r"""Machine identifier of the Plex server.""" + + name: str + r"""Name of the Plex server of the connected user.""" + + last_seen_at: int + + num_libraries: int + r"""Number of libraries in the server this user has access to.""" + + all_libraries: Optional[AllLibraries] = AllLibraries.DISABLE + + owned: Optional[Owned] = Owned.DISABLE + + pending: Optional[Pending] = Pending.DISABLE + + +class UserTypedDict(TypedDict): + id: int + r"""User's unique ID.""" + title: str + r"""User's display name.""" + username: str + r"""User's username.""" + email: str + r"""User's email address.""" + thumb: str + r"""URL to the user's avatar image.""" + server: List[GetUsersServerTypedDict] + r"""List of servers owned by the user.""" + recommendations_playlist_id: NotRequired[Nullable[str]] + r"""ID of the user's recommendation playlist.""" + protected: NotRequired[Protected] + home: NotRequired[Home] + allow_tuners: NotRequired[AllowTuners] + allow_sync: NotRequired[AllowSync] + allow_camera_upload: NotRequired[AllowCameraUpload] + allow_channels: NotRequired[AllowChannels] + allow_subtitle_admin: NotRequired[AllowSubtitleAdmin] + filter_all: NotRequired[Nullable[str]] + r"""Filters applied for all content.""" + filter_movies: NotRequired[Nullable[str]] + r"""Filters applied for movies.""" + filter_music: NotRequired[Nullable[str]] + r"""Filters applied for music.""" + filter_photos: NotRequired[Nullable[str]] + r"""Filters applied for photos.""" + filter_television: NotRequired[str] + r"""Filters applied for television.""" + restricted: NotRequired[Restricted] + + +class User(BaseModel): + id: int + r"""User's unique ID.""" + + title: str + r"""User's display name.""" + + username: str + r"""User's username.""" + + email: str + r"""User's email address.""" + + thumb: str + r"""URL to the user's avatar image.""" + + server: List[GetUsersServer] + r"""List of servers owned by the user.""" + + recommendations_playlist_id: OptionalNullable[str] = UNSET + r"""ID of the user's recommendation playlist.""" + + protected: Optional[Protected] = Protected.DISABLE + + home: Optional[Home] = Home.DISABLE + + allow_tuners: Optional[AllowTuners] = AllowTuners.DISABLE + + allow_sync: Optional[AllowSync] = AllowSync.DISABLE + + allow_camera_upload: Optional[AllowCameraUpload] = AllowCameraUpload.DISABLE + + allow_channels: Optional[AllowChannels] = AllowChannels.DISABLE + + allow_subtitle_admin: Optional[AllowSubtitleAdmin] = AllowSubtitleAdmin.DISABLE + + filter_all: OptionalNullable[str] = UNSET + r"""Filters applied for all content.""" + + filter_movies: OptionalNullable[str] = UNSET + r"""Filters applied for movies.""" + + filter_music: OptionalNullable[str] = UNSET + r"""Filters applied for music.""" + + filter_photos: OptionalNullable[str] = UNSET + r"""Filters applied for photos.""" + + filter_television: Optional[str] = None + r"""Filters applied for television.""" + + restricted: Optional[Restricted] = Restricted.DISABLE + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = [ + "recommendationsPlaylistId", + "protected", + "home", + "allowTuners", + "allowSync", + "allowCameraUpload", + "allowChannels", + "allowSubtitleAdmin", + "filterAll", + "filterMovies", + "filterMusic", + "filterPhotos", + "filterTelevision", + "restricted", + ] + nullable_fields = [ + "recommendationsPlaylistId", + "filterAll", + "filterMovies", + "filterMusic", + "filterPhotos", + ] + null_default_fields = [] + + serialized = handler(self) + + m = {} + + for n, f in self.model_fields.items(): + k = f.alias or n + val = serialized.get(k) + serialized.pop(k, None) + + optional_nullable = k in optional_fields and k in nullable_fields + is_set = ( + self.__pydantic_fields_set__.intersection({n}) + or k in null_default_fields + ) # pylint: disable=no-member + + if val is not None and val != UNSET_SENTINEL: + m[k] = val + elif val != UNSET_SENTINEL and ( + not k in optional_fields or (optional_nullable and is_set) + ): + m[k] = val + + return m + + +class GetUsersMediaContainerTypedDict(TypedDict): + r"""Container holding user and server details.""" + + friendly_name: str + r"""The friendly name of the Plex instance.""" + identifier: str + machine_identifier: str + r"""Unique Machine identifier of the Plex server.""" + total_size: int + r"""Total number of users.""" + size: int + r"""Number of users in the current response.""" + user: List[UserTypedDict] + r"""List of users with access to the Plex server.""" + + +class GetUsersMediaContainer(BaseModel): + r"""Container holding user and server details.""" + + friendly_name: str + r"""The friendly name of the Plex instance.""" + + identifier: str + + machine_identifier: str + r"""Unique Machine identifier of the Plex server.""" + + total_size: int + r"""Total number of users.""" + + size: int + r"""Number of users in the current response.""" + + user: List[User] + r"""List of users with access to the Plex server.""" + + +class GetUsersResponseBodyTypedDict(TypedDict): + r"""Successful response with media container data in XML""" + + media_container: NotRequired[GetUsersMediaContainerTypedDict] + r"""Container holding user and server details.""" + + +class GetUsersResponseBody(BaseModel): + r"""Successful response with media container data in XML""" + + media_container: Optional[GetUsersMediaContainer] = None + r"""Container holding user and server details.""" + + +class GetUsersResponseTypedDict(TypedDict): + content_type: str + r"""HTTP response content type for this operation""" + status_code: int + r"""HTTP response status code for this operation""" + raw_response: httpx.Response + r"""Raw HTTP response; suitable for custom response parsing""" + body: NotRequired[bytes] + + +class GetUsersResponse(BaseModel): + content_type: str + r"""HTTP response content type for this operation""" + + status_code: int + r"""HTTP response status code for this operation""" + + raw_response: httpx.Response + r"""Raw HTTP response; suitable for custom response parsing""" + + body: Optional[bytes] = None diff --git a/src/plex_api_client/models/operations/gettopwatchedcontent.py b/src/plex_api_client/models/operations/gettopwatchedcontent.py index 0832068..34d9e3c 100644 --- a/src/plex_api_client/models/operations/gettopwatchedcontent.py +++ b/src/plex_api_client/models/operations/gettopwatchedcontent.py @@ -129,11 +129,11 @@ class GetTopWatchedContentRole(BaseModel): thumb: Optional[str] = None -class UserTypedDict(TypedDict): +class GetTopWatchedContentUserTypedDict(TypedDict): id: NotRequired[int] -class User(BaseModel): +class GetTopWatchedContentUser(BaseModel): id: Optional[int] = None @@ -169,7 +169,7 @@ class GetTopWatchedContentMetadataTypedDict(TypedDict): country: NotRequired[List[GetTopWatchedContentCountryTypedDict]] guids: NotRequired[List[GetTopWatchedContentGuidsTypedDict]] role: NotRequired[List[GetTopWatchedContentRoleTypedDict]] - user: NotRequired[List[UserTypedDict]] + user: NotRequired[List[GetTopWatchedContentUserTypedDict]] class GetTopWatchedContentMetadata(BaseModel): @@ -261,7 +261,9 @@ class GetTopWatchedContentMetadata(BaseModel): Optional[List[GetTopWatchedContentRole]], pydantic.Field(alias="Role") ] = None - user: Annotated[Optional[List[User]], pydantic.Field(alias="User")] = None + user: Annotated[ + Optional[List[GetTopWatchedContentUser]], pydantic.Field(alias="User") + ] = None class GetTopWatchedContentMediaContainerTypedDict(TypedDict): diff --git a/src/plex_api_client/sdk.py b/src/plex_api_client/sdk.py index cbc0075..6aa1f4f 100644 --- a/src/plex_api_client/sdk.py +++ b/src/plex_api_client/sdk.py @@ -24,6 +24,7 @@ from plex_api_client.sessions import Sessions from plex_api_client.statistics import Statistics from plex_api_client.types import OptionalNullable, UNSET from plex_api_client.updater import Updater +from plex_api_client.users import Users from plex_api_client.video import Video from plex_api_client.watchlist import Watchlist from typing import Any, Callable, Dict, List, Optional, Union, cast @@ -134,6 +135,7 @@ class PlexAPI(BaseSDK): Updates to the status can be observed via the Event API. """ + users: Users def __init__( self, @@ -255,6 +257,7 @@ class PlexAPI(BaseSDK): self.statistics = Statistics(self.sdk_configuration) self.sessions = Sessions(self.sdk_configuration) self.updater = Updater(self.sdk_configuration) + self.users = Users(self.sdk_configuration) def __enter__(self): return self diff --git a/src/plex_api_client/users.py b/src/plex_api_client/users.py new file mode 100644 index 0000000..e654644 --- /dev/null +++ b/src/plex_api_client/users.py @@ -0,0 +1,222 @@ +"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" + +from .basesdk import BaseSDK +from plex_api_client import utils +from plex_api_client._hooks import HookContext +from plex_api_client.models import errors, operations +from plex_api_client.types import BaseModel, OptionalNullable, UNSET +from typing import Any, Mapping, Optional, Union, cast + + +class Users(BaseSDK): + def get_users( + self, + *, + request: Union[operations.GetUsersRequest, operations.GetUsersRequestTypedDict], + retries: OptionalNullable[utils.RetryConfig] = UNSET, + server_url: Optional[str] = None, + timeout_ms: Optional[int] = None, + http_headers: Optional[Mapping[str, str]] = None, + ) -> operations.GetUsersResponse: + r"""Get list of all connected users + + Get list of all users that are friends and have library access with the provided Plex authentication token + + :param request: The request object to send. + :param retries: Override the default retry configuration for this method + :param server_url: Override the default server URL for this method + :param timeout_ms: Override the default request timeout configuration for this method in milliseconds + :param http_headers: Additional headers to set or replace on requests. + """ + base_url = None + url_variables = None + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = operations.GET_USERS_SERVERS[0] + + if not isinstance(request, BaseModel): + request = utils.unmarshal(request, operations.GetUsersRequest) + request = cast(operations.GetUsersRequest, request) + + req = self._build_request( + method="GET", + path="/users", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=False, + request_has_query_params=False, + user_agent_header="user-agent", + accept_header_value="application/xml", + http_headers=http_headers, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["429", "500", "502", "503", "504"]) + + http_res = self.do_request( + hook_ctx=HookContext( + operation_id="get-users", oauth2_scopes=[], security_source=None + ), + request=req, + error_status_codes=["400", "401", "4XX", "5XX"], + retry_config=retry_config, + ) + + response_data: Any = None + if utils.match_response(http_res, "200", "application/xml"): + http_res_bytes = utils.stream_to_bytes(http_res) + return operations.GetUsersResponse( + body=http_res_bytes, + status_code=http_res.status_code, + content_type=http_res.headers.get("Content-Type") or "", + raw_response=http_res, + ) + if utils.match_response(http_res, "400", "application/json"): + response_data = utils.unmarshal_json( + http_res.text, errors.GetUsersBadRequestData + ) + response_data.raw_response = http_res + raise errors.GetUsersBadRequest(data=response_data) + if utils.match_response(http_res, "401", "application/json"): + response_data = utils.unmarshal_json( + http_res.text, errors.GetUsersUnauthorizedData + ) + response_data.raw_response = http_res + raise errors.GetUsersUnauthorized(data=response_data) + if utils.match_response(http_res, "4XX", "*"): + http_res_text = utils.stream_to_text(http_res) + raise errors.SDKError( + "API error occurred", http_res.status_code, http_res_text, http_res + ) + if utils.match_response(http_res, "5XX", "*"): + http_res_text = utils.stream_to_text(http_res) + raise errors.SDKError( + "API error occurred", http_res.status_code, http_res_text, http_res + ) + + content_type = http_res.headers.get("Content-Type") + http_res_text = utils.stream_to_text(http_res) + raise errors.SDKError( + f"Unexpected response received (code: {http_res.status_code}, type: {content_type})", + http_res.status_code, + http_res_text, + http_res, + ) + + async def get_users_async( + self, + *, + request: Union[operations.GetUsersRequest, operations.GetUsersRequestTypedDict], + retries: OptionalNullable[utils.RetryConfig] = UNSET, + server_url: Optional[str] = None, + timeout_ms: Optional[int] = None, + http_headers: Optional[Mapping[str, str]] = None, + ) -> operations.GetUsersResponse: + r"""Get list of all connected users + + Get list of all users that are friends and have library access with the provided Plex authentication token + + :param request: The request object to send. + :param retries: Override the default retry configuration for this method + :param server_url: Override the default server URL for this method + :param timeout_ms: Override the default request timeout configuration for this method in milliseconds + :param http_headers: Additional headers to set or replace on requests. + """ + base_url = None + url_variables = None + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = operations.GET_USERS_SERVERS[0] + + if not isinstance(request, BaseModel): + request = utils.unmarshal(request, operations.GetUsersRequest) + request = cast(operations.GetUsersRequest, request) + + req = self._build_request_async( + method="GET", + path="/users", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=False, + request_has_query_params=False, + user_agent_header="user-agent", + accept_header_value="application/xml", + http_headers=http_headers, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["429", "500", "502", "503", "504"]) + + http_res = await self.do_request_async( + hook_ctx=HookContext( + operation_id="get-users", oauth2_scopes=[], security_source=None + ), + request=req, + error_status_codes=["400", "401", "4XX", "5XX"], + retry_config=retry_config, + ) + + response_data: Any = None + if utils.match_response(http_res, "200", "application/xml"): + http_res_bytes = await utils.stream_to_bytes_async(http_res) + return operations.GetUsersResponse( + body=http_res_bytes, + status_code=http_res.status_code, + content_type=http_res.headers.get("Content-Type") or "", + raw_response=http_res, + ) + if utils.match_response(http_res, "400", "application/json"): + response_data = utils.unmarshal_json( + http_res.text, errors.GetUsersBadRequestData + ) + response_data.raw_response = http_res + raise errors.GetUsersBadRequest(data=response_data) + if utils.match_response(http_res, "401", "application/json"): + response_data = utils.unmarshal_json( + http_res.text, errors.GetUsersUnauthorizedData + ) + response_data.raw_response = http_res + raise errors.GetUsersUnauthorized(data=response_data) + if utils.match_response(http_res, "4XX", "*"): + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.SDKError( + "API error occurred", http_res.status_code, http_res_text, http_res + ) + if utils.match_response(http_res, "5XX", "*"): + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.SDKError( + "API error occurred", http_res.status_code, http_res_text, http_res + ) + + content_type = http_res.headers.get("Content-Type") + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.SDKError( + f"Unexpected response received (code: {http_res.status_code}, type: {content_type})", + http_res.status_code, + http_res_text, + http_res, + )