plex-api-client 0.6.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. plex_api/__init__.py +4 -0
  2. plex_api/_hooks/__init__.py +5 -0
  3. plex_api/_hooks/registration.py +13 -0
  4. plex_api/_hooks/sdkhooks.py +57 -0
  5. plex_api/_hooks/types.py +74 -0
  6. plex_api/activities.py +151 -0
  7. plex_api/authentication.py +146 -0
  8. plex_api/butler.py +333 -0
  9. plex_api/hubs.py +158 -0
  10. plex_api/library.py +808 -0
  11. plex_api/log.py +225 -0
  12. plex_api/media.py +210 -0
  13. plex_api/models/__init__.py +4 -0
  14. plex_api/models/components/__init__.py +5 -0
  15. plex_api/models/components/security.py +11 -0
  16. plex_api/models/errors/__init__.py +63 -0
  17. plex_api/models/errors/addplaylistcontents.py +32 -0
  18. plex_api/models/errors/applyupdates.py +32 -0
  19. plex_api/models/errors/cancelserveractivities.py +32 -0
  20. plex_api/models/errors/checkforupdates.py +32 -0
  21. plex_api/models/errors/clearplaylistcontents.py +32 -0
  22. plex_api/models/errors/createplaylist.py +32 -0
  23. plex_api/models/errors/deletelibrary.py +32 -0
  24. plex_api/models/errors/deleteplaylist.py +32 -0
  25. plex_api/models/errors/enablepapertrail.py +32 -0
  26. plex_api/models/errors/getavailableclients.py +32 -0
  27. plex_api/models/errors/getbutlertasks.py +32 -0
  28. plex_api/models/errors/getdevices.py +32 -0
  29. plex_api/models/errors/getfilehash.py +32 -0
  30. plex_api/models/errors/getglobalhubs.py +32 -0
  31. plex_api/models/errors/getlibraries.py +32 -0
  32. plex_api/models/errors/getlibrary.py +32 -0
  33. plex_api/models/errors/getlibraryhubs.py +32 -0
  34. plex_api/models/errors/getmetadata.py +32 -0
  35. plex_api/models/errors/getmetadatachildren.py +32 -0
  36. plex_api/models/errors/getmyplexaccount.py +32 -0
  37. plex_api/models/errors/getondeck.py +32 -0
  38. plex_api/models/errors/getpin.py +32 -0
  39. plex_api/models/errors/getplaylist.py +32 -0
  40. plex_api/models/errors/getplaylistcontents.py +32 -0
  41. plex_api/models/errors/getplaylists.py +32 -0
  42. plex_api/models/errors/getrecentlyadded.py +32 -0
  43. plex_api/models/errors/getresizedphoto.py +32 -0
  44. plex_api/models/errors/getsearchresults.py +32 -0
  45. plex_api/models/errors/getserveractivities.py +32 -0
  46. plex_api/models/errors/getservercapabilities.py +32 -0
  47. plex_api/models/errors/getserveridentity.py +32 -0
  48. plex_api/models/errors/getserverlist.py +32 -0
  49. plex_api/models/errors/getserverpreferences.py +32 -0
  50. plex_api/models/errors/getsessionhistory.py +32 -0
  51. plex_api/models/errors/getsessions.py +32 -0
  52. plex_api/models/errors/getsourceconnectioninformation.py +32 -0
  53. plex_api/models/errors/getstatistics.py +32 -0
  54. plex_api/models/errors/gettimeline.py +32 -0
  55. plex_api/models/errors/gettoken.py +32 -0
  56. plex_api/models/errors/gettranscodesessions.py +32 -0
  57. plex_api/models/errors/gettransienttoken.py +32 -0
  58. plex_api/models/errors/getupdatestatus.py +32 -0
  59. plex_api/models/errors/logline.py +32 -0
  60. plex_api/models/errors/logmultiline.py +32 -0
  61. plex_api/models/errors/markplayed.py +32 -0
  62. plex_api/models/errors/markunplayed.py +32 -0
  63. plex_api/models/errors/performsearch.py +32 -0
  64. plex_api/models/errors/performvoicesearch.py +32 -0
  65. plex_api/models/errors/refreshlibrary.py +32 -0
  66. plex_api/models/errors/sdkerror.py +24 -0
  67. plex_api/models/errors/startalltasks.py +32 -0
  68. plex_api/models/errors/starttask.py +32 -0
  69. plex_api/models/errors/startuniversaltranscode.py +32 -0
  70. plex_api/models/errors/stopalltasks.py +32 -0
  71. plex_api/models/errors/stoptask.py +32 -0
  72. plex_api/models/errors/stoptranscodesession.py +32 -0
  73. plex_api/models/errors/updateplaylist.py +32 -0
  74. plex_api/models/errors/updateplayprogress.py +32 -0
  75. plex_api/models/errors/uploadplaylist.py +32 -0
  76. plex_api/models/operations/__init__.py +64 -0
  77. plex_api/models/operations/addplaylistcontents.py +74 -0
  78. plex_api/models/operations/applyupdates.py +39 -0
  79. plex_api/models/operations/cancelserveractivities.py +25 -0
  80. plex_api/models/operations/checkforupdates.py +32 -0
  81. plex_api/models/operations/clearplaylistcontents.py +25 -0
  82. plex_api/models/operations/createplaylist.py +91 -0
  83. plex_api/models/operations/deletelibrary.py +25 -0
  84. plex_api/models/operations/deleteplaylist.py +25 -0
  85. plex_api/models/operations/enablepapertrail.py +17 -0
  86. plex_api/models/operations/getavailableclients.py +58 -0
  87. plex_api/models/operations/getbutlertasks.py +52 -0
  88. plex_api/models/operations/getdevices.py +53 -0
  89. plex_api/models/operations/getfilehash.py +28 -0
  90. plex_api/models/operations/getglobalhubs.py +100 -0
  91. plex_api/models/operations/getlibraries.py +79 -0
  92. plex_api/models/operations/getlibrary.py +153 -0
  93. plex_api/models/operations/getlibraryhubs.py +205 -0
  94. plex_api/models/operations/getlibraryitems.py +227 -0
  95. plex_api/models/operations/getmetadata.py +264 -0
  96. plex_api/models/operations/getmetadatachildren.py +115 -0
  97. plex_api/models/operations/getmyplexaccount.py +50 -0
  98. plex_api/models/operations/getondeck.py +166 -0
  99. plex_api/models/operations/getpin.py +86 -0
  100. plex_api/models/operations/getplaylist.py +70 -0
  101. plex_api/models/operations/getplaylistcontents.py +178 -0
  102. plex_api/models/operations/getplaylists.py +85 -0
  103. plex_api/models/operations/getrecentlyadded.py +166 -0
  104. plex_api/models/operations/getresizedphoto.py +48 -0
  105. plex_api/models/operations/getsearchresults.py +182 -0
  106. plex_api/models/operations/getserveractivities.py +63 -0
  107. plex_api/models/operations/getservercapabilities.py +99 -0
  108. plex_api/models/operations/getserveridentity.py +42 -0
  109. plex_api/models/operations/getserverlist.py +53 -0
  110. plex_api/models/operations/getserverpreferences.py +57 -0
  111. plex_api/models/operations/getsessionhistory.py +67 -0
  112. plex_api/models/operations/getsessions.py +185 -0
  113. plex_api/models/operations/getsourceconnectioninformation.py +25 -0
  114. plex_api/models/operations/getstatistics.py +93 -0
  115. plex_api/models/operations/gettimeline.py +50 -0
  116. plex_api/models/operations/gettoken.py +35 -0
  117. plex_api/models/operations/gettranscodesessions.py +71 -0
  118. plex_api/models/operations/gettransienttoken.py +36 -0
  119. plex_api/models/operations/getupdatestatus.py +57 -0
  120. plex_api/models/operations/logline.py +50 -0
  121. plex_api/models/operations/logmultiline.py +17 -0
  122. plex_api/models/operations/markplayed.py +25 -0
  123. plex_api/models/operations/markunplayed.py +25 -0
  124. plex_api/models/operations/performsearch.py +30 -0
  125. plex_api/models/operations/performvoicesearch.py +30 -0
  126. plex_api/models/operations/refreshlibrary.py +25 -0
  127. plex_api/models/operations/searchlibrary.py +96 -0
  128. plex_api/models/operations/startalltasks.py +17 -0
  129. plex_api/models/operations/starttask.py +43 -0
  130. plex_api/models/operations/startuniversaltranscode.py +56 -0
  131. plex_api/models/operations/stopalltasks.py +17 -0
  132. plex_api/models/operations/stoptask.py +43 -0
  133. plex_api/models/operations/stoptranscodesession.py +25 -0
  134. plex_api/models/operations/updateplaylist.py +30 -0
  135. plex_api/models/operations/updateplayprogress.py +29 -0
  136. plex_api/models/operations/uploadplaylist.py +47 -0
  137. plex_api/playlists.py +629 -0
  138. plex_api/plex.py +154 -0
  139. plex_api/sdk.py +180 -0
  140. plex_api/sdkconfiguration.py +52 -0
  141. plex_api/search.py +232 -0
  142. plex_api/server.py +525 -0
  143. plex_api/sessions.py +272 -0
  144. plex_api/statistics.py +86 -0
  145. plex_api/updater.py +212 -0
  146. plex_api/utils/__init__.py +4 -0
  147. plex_api/utils/retries.py +120 -0
  148. plex_api/utils/utils.py +898 -0
  149. plex_api/video.py +136 -0
  150. plex_api_client-0.6.1.dist-info/LICENSE.md +21 -0
  151. plex_api_client-0.6.1.dist-info/METADATA +397 -0
  152. plex_api_client-0.6.1.dist-info/RECORD +154 -0
  153. plex_api_client-0.6.1.dist-info/WHEEL +5 -0
  154. plex_api_client-0.6.1.dist-info/top_level.txt +1 -0
plex_api/plex.py ADDED
@@ -0,0 +1,154 @@
1
+ """Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT."""
2
+
3
+ import requests as requests_http
4
+ from .sdkconfiguration import SDKConfiguration
5
+ from plex_api import utils
6
+ from plex_api._hooks import AfterErrorContext, AfterSuccessContext, BeforeRequestContext, HookContext
7
+ from plex_api.models import errors, operations
8
+ from typing import Optional
9
+
10
+ class Plex:
11
+ r"""API Calls that perform operations directly against https://Plex.tv"""
12
+ sdk_configuration: SDKConfiguration
13
+
14
+ def __init__(self, sdk_config: SDKConfiguration) -> None:
15
+ self.sdk_configuration = sdk_config
16
+
17
+
18
+
19
+ def get_pin(self, strong: Optional[bool] = None, x_plex_client_identifier: Optional[str] = None, server_url: Optional[str] = None) -> operations.GetPinResponse:
20
+ r"""Get a Pin
21
+ Retrieve a Pin from Plex.tv for authentication flows
22
+ """
23
+ hook_ctx = HookContext(operation_id='getPin', oauth2_scopes=[], security_source=None)
24
+ request = operations.GetPinRequest(
25
+ strong=strong,
26
+ x_plex_client_identifier=x_plex_client_identifier,
27
+ )
28
+
29
+ base_url = utils.template_url(operations.GET_PIN_SERVERS[0], {
30
+ })
31
+ if server_url is not None:
32
+ base_url = server_url
33
+
34
+ url = base_url + '/pins'
35
+
36
+ headers = {}
37
+ query_params = {}
38
+
39
+ headers = { **utils.get_headers(request, self.sdk_configuration.globals), **headers }
40
+ query_params = { **utils.get_query_params(operations.GetPinRequest, request, self.sdk_configuration.globals), **query_params }
41
+ headers['Accept'] = 'application/json'
42
+ headers['user-agent'] = self.sdk_configuration.user_agent
43
+ client = self.sdk_configuration.client
44
+
45
+ try:
46
+ req = client.prepare_request(requests_http.Request('POST', url, params=query_params, headers=headers))
47
+ req = self.sdk_configuration.get_hooks().before_request(BeforeRequestContext(hook_ctx), req)
48
+ http_res = client.send(req)
49
+ except Exception as e:
50
+ _, e = self.sdk_configuration.get_hooks().after_error(AfterErrorContext(hook_ctx), None, e)
51
+ if e is not None:
52
+ raise e
53
+
54
+ if utils.match_status_codes(['400','4XX','5XX'], http_res.status_code):
55
+ result, e = self.sdk_configuration.get_hooks().after_error(AfterErrorContext(hook_ctx), http_res, None)
56
+ if e is not None:
57
+ raise e
58
+ if result is not None:
59
+ http_res = result
60
+ else:
61
+ http_res = self.sdk_configuration.get_hooks().after_success(AfterSuccessContext(hook_ctx), http_res)
62
+
63
+
64
+
65
+ res = operations.GetPinResponse(status_code=http_res.status_code, content_type=http_res.headers.get('Content-Type') or '', raw_response=http_res)
66
+
67
+ if http_res.status_code == 200:
68
+ if utils.match_content_type(http_res.headers.get('Content-Type') or '', 'application/json'):
69
+ out = utils.unmarshal_json(http_res.text, Optional[operations.GetPinResponseBody])
70
+ res.object = out
71
+ else:
72
+ content_type = http_res.headers.get('Content-Type')
73
+ raise errors.SDKError(f'unknown content-type received: {content_type}', http_res.status_code, http_res.text, http_res)
74
+ elif http_res.status_code == 400:
75
+ if utils.match_content_type(http_res.headers.get('Content-Type') or '', 'application/json'):
76
+ out = utils.unmarshal_json(http_res.text, errors.GetPinResponseBody)
77
+ out.raw_response = http_res
78
+ raise out
79
+ else:
80
+ content_type = http_res.headers.get('Content-Type')
81
+ raise errors.SDKError(f'unknown content-type received: {content_type}', http_res.status_code, http_res.text, http_res)
82
+ elif http_res.status_code >= 400 and http_res.status_code < 500 or http_res.status_code >= 500 and http_res.status_code < 600:
83
+ raise errors.SDKError('API error occurred', http_res.status_code, http_res.text, http_res)
84
+ else:
85
+ raise errors.SDKError('unknown status code received', http_res.status_code, http_res.text, http_res)
86
+
87
+ return res
88
+
89
+
90
+
91
+ def get_token(self, pin_id: str, x_plex_client_identifier: Optional[str] = None, server_url: Optional[str] = None) -> operations.GetTokenResponse:
92
+ r"""Get Access Token
93
+ Retrieve an Access Token from Plex.tv after the Pin has already been authenticated
94
+ """
95
+ hook_ctx = HookContext(operation_id='getToken', oauth2_scopes=[], security_source=None)
96
+ request = operations.GetTokenRequest(
97
+ pin_id=pin_id,
98
+ x_plex_client_identifier=x_plex_client_identifier,
99
+ )
100
+
101
+ base_url = utils.template_url(operations.GET_TOKEN_SERVERS[0], {
102
+ })
103
+ if server_url is not None:
104
+ base_url = server_url
105
+
106
+ url = utils.generate_url(operations.GetTokenRequest, base_url, '/pins/{pinID}', request, self.sdk_configuration.globals)
107
+
108
+ headers = {}
109
+
110
+ headers = { **utils.get_headers(request, self.sdk_configuration.globals), **headers }
111
+ headers['Accept'] = 'application/json'
112
+ headers['user-agent'] = self.sdk_configuration.user_agent
113
+ client = self.sdk_configuration.client
114
+
115
+ try:
116
+ req = client.prepare_request(requests_http.Request('GET', url, headers=headers))
117
+ req = self.sdk_configuration.get_hooks().before_request(BeforeRequestContext(hook_ctx), req)
118
+ http_res = client.send(req)
119
+ except Exception as e:
120
+ _, e = self.sdk_configuration.get_hooks().after_error(AfterErrorContext(hook_ctx), None, e)
121
+ if e is not None:
122
+ raise e
123
+
124
+ if utils.match_status_codes(['400','4XX','5XX'], http_res.status_code):
125
+ result, e = self.sdk_configuration.get_hooks().after_error(AfterErrorContext(hook_ctx), http_res, None)
126
+ if e is not None:
127
+ raise e
128
+ if result is not None:
129
+ http_res = result
130
+ else:
131
+ http_res = self.sdk_configuration.get_hooks().after_success(AfterSuccessContext(hook_ctx), http_res)
132
+
133
+
134
+
135
+ res = operations.GetTokenResponse(status_code=http_res.status_code, content_type=http_res.headers.get('Content-Type') or '', raw_response=http_res)
136
+
137
+ if http_res.status_code == 200:
138
+ pass
139
+ elif http_res.status_code == 400:
140
+ if utils.match_content_type(http_res.headers.get('Content-Type') or '', 'application/json'):
141
+ out = utils.unmarshal_json(http_res.text, errors.GetTokenResponseBody)
142
+ out.raw_response = http_res
143
+ raise out
144
+ else:
145
+ content_type = http_res.headers.get('Content-Type')
146
+ raise errors.SDKError(f'unknown content-type received: {content_type}', http_res.status_code, http_res.text, http_res)
147
+ elif http_res.status_code >= 400 and http_res.status_code < 500 or http_res.status_code >= 500 and http_res.status_code < 600:
148
+ raise errors.SDKError('API error occurred', http_res.status_code, http_res.text, http_res)
149
+ else:
150
+ raise errors.SDKError('unknown status code received', http_res.status_code, http_res.text, http_res)
151
+
152
+ return res
153
+
154
+
plex_api/sdk.py ADDED
@@ -0,0 +1,180 @@
1
+ """Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT."""
2
+
3
+ import requests as requests_http
4
+ from .activities import Activities
5
+ from .authentication import Authentication
6
+ from .butler import Butler
7
+ from .hubs import Hubs
8
+ from .library import Library
9
+ from .log import Log
10
+ from .media import Media
11
+ from .playlists import Playlists
12
+ from .plex import Plex
13
+ from .sdkconfiguration import SDKConfiguration, ServerProtocol
14
+ from .search import Search
15
+ from .server import Server
16
+ from .sessions import Sessions
17
+ from .statistics import Statistics
18
+ from .updater import Updater
19
+ from .utils.retries import RetryConfig
20
+ from .video import Video
21
+ from plex_api import utils
22
+ from plex_api._hooks import SDKHooks
23
+ from plex_api.models import components
24
+ from typing import Callable, Dict, Optional, Union
25
+
26
+ class PlexAPI:
27
+ r"""Plex-API: A Plex Media Server API Map
28
+ An Open API Spec for interacting with Plex.tv and Plex Servers
29
+ """
30
+ server: Server
31
+ r"""Operations against the Plex Media Server System."""
32
+ media: Media
33
+ r"""API Calls interacting with Plex Media Server Media"""
34
+ video: Video
35
+ r"""API Calls that perform operations with Plex Media Server Videos"""
36
+ activities: Activities
37
+ r"""Activities are awesome. They provide a way to monitor and control asynchronous operations on the server. In order to receive real-time updates for activities, a client would normally subscribe via either EventSource or Websocket endpoints.
38
+ Activities are associated with HTTP replies via a special `X-Plex-Activity` header which contains the UUID of the activity.
39
+ Activities are optional cancellable. If cancellable, they may be cancelled via the `DELETE` endpoint. Other details:
40
+ - They can contain a `progress` (from 0 to 100) marking the percent completion of the activity.
41
+ - They must contain an `type` which is used by clients to distinguish the specific activity.
42
+ - They may contain a `Context` object with attributes which associate the activity with various specific entities (items, libraries, etc.)
43
+ - The may contain a `Response` object which attributes which represent the result of the asynchronous operation.
44
+ """
45
+ butler: Butler
46
+ r"""Butler is the task manager of the Plex Media Server Ecosystem."""
47
+ hubs: Hubs
48
+ r"""Hubs are a structured two-dimensional container for media, generally represented by multiple horizontal rows."""
49
+ search: Search
50
+ r"""API Calls that perform search operations with Plex Media Server"""
51
+ library: Library
52
+ r"""API Calls interacting with Plex Media Server Libraries"""
53
+ log: Log
54
+ r"""Submit logs to the Log Handler for Plex Media Server"""
55
+ plex: Plex
56
+ r"""API Calls that perform operations directly against https://Plex.tv"""
57
+ playlists: Playlists
58
+ r"""Playlists are ordered collections of media. They can be dumb (just a list of media) or smart (based on a media query, such as \\"all albums from 2017\\").
59
+ They can be organized in (optionally nesting) folders.
60
+ Retrieving a playlist, or its items, will trigger a refresh of its metadata.
61
+ This may cause the duration and number of items to change.
62
+ """
63
+ authentication: Authentication
64
+ r"""API Calls regarding authentication for Plex Media Server"""
65
+ statistics: Statistics
66
+ r"""API Calls that perform operations with Plex Media Server Statistics"""
67
+ sessions: Sessions
68
+ r"""API Calls that perform search operations with Plex Media Server Sessions"""
69
+ updater: Updater
70
+ r"""This describes the API for searching and applying updates to the Plex Media Server.
71
+ Updates to the status can be observed via the Event API.
72
+ """
73
+
74
+ sdk_configuration: SDKConfiguration
75
+
76
+ def __init__(self,
77
+ access_token: Union[str, Callable[[], str]],
78
+ x_plex_client_identifier: str = None,
79
+ protocol: ServerProtocol = None,
80
+ ip: str = None,
81
+ port: str = None,
82
+ server_idx: Optional[int] = None,
83
+ server_url: Optional[str] = None,
84
+ url_params: Optional[Dict[str, str]] = None,
85
+ client: Optional[requests_http.Session] = None,
86
+ retry_config: Optional[RetryConfig] = None
87
+ ) -> None:
88
+ """Instantiates the SDK configuring it with the provided parameters.
89
+
90
+ :param access_token: The access_token required for authentication
91
+ :type access_token: Union[str, Callable[[], str]]
92
+ :param x_plex_client_identifier: Configures the x_plex_client_identifier parameter for all supported operations
93
+ :type x_plex_client_identifier: str
94
+ :param protocol: Allows setting the protocol variable for url substitution
95
+ :type protocol: ServerProtocol
96
+ :param ip: Allows setting the ip variable for url substitution
97
+ :type ip: str
98
+ :param port: Allows setting the port variable for url substitution
99
+ :type port: str
100
+ :param server_idx: The index of the server to use for all operations
101
+ :type server_idx: int
102
+ :param server_url: The server URL to use for all operations
103
+ :type server_url: str
104
+ :param url_params: Parameters to optionally template the server URL with
105
+ :type url_params: Dict[str, str]
106
+ :param client: The requests.Session HTTP client to use for all operations
107
+ :type client: requests_http.Session
108
+ :param retry_config: The utils.RetryConfig to use globally
109
+ :type retry_config: RetryConfig
110
+ """
111
+ if client is None:
112
+ client = requests_http.Session()
113
+
114
+ if callable(access_token):
115
+ def security():
116
+ return components.Security(access_token = access_token())
117
+ else:
118
+ security = components.Security(access_token = access_token)
119
+
120
+ if server_url is not None:
121
+ if url_params is not None:
122
+ server_url = utils.template_url(server_url, url_params)
123
+ server_defaults = [
124
+ {
125
+ 'protocol': protocol or 'http',
126
+ 'ip': ip or '10.10.10.47',
127
+ 'port': port or '32400',
128
+ },
129
+ ]
130
+ global_params = {
131
+ 'parameters': {
132
+ 'queryParam': {
133
+ },
134
+ 'pathParam': {
135
+ },
136
+ 'header': {
137
+ 'x_plex_client_identifier': x_plex_client_identifier,
138
+ },
139
+ },
140
+ }
141
+
142
+ self.sdk_configuration = SDKConfiguration(
143
+ client,
144
+ security,
145
+ server_url,
146
+ server_idx,
147
+ server_defaults,
148
+ global_params,
149
+ retry_config=retry_config
150
+ )
151
+
152
+ hooks = SDKHooks()
153
+
154
+ current_server_url, *_ = self.sdk_configuration.get_server_details()
155
+ server_url, self.sdk_configuration.client = hooks.sdk_init(current_server_url, self.sdk_configuration.client)
156
+ if current_server_url != server_url:
157
+ self.sdk_configuration.server_url = server_url
158
+
159
+ # pylint: disable=protected-access
160
+ self.sdk_configuration._hooks = hooks
161
+
162
+ self._init_sdks()
163
+
164
+
165
+ def _init_sdks(self):
166
+ self.server = Server(self.sdk_configuration)
167
+ self.media = Media(self.sdk_configuration)
168
+ self.video = Video(self.sdk_configuration)
169
+ self.activities = Activities(self.sdk_configuration)
170
+ self.butler = Butler(self.sdk_configuration)
171
+ self.hubs = Hubs(self.sdk_configuration)
172
+ self.search = Search(self.sdk_configuration)
173
+ self.library = Library(self.sdk_configuration)
174
+ self.log = Log(self.sdk_configuration)
175
+ self.plex = Plex(self.sdk_configuration)
176
+ self.playlists = Playlists(self.sdk_configuration)
177
+ self.authentication = Authentication(self.sdk_configuration)
178
+ self.statistics = Statistics(self.sdk_configuration)
179
+ self.sessions = Sessions(self.sdk_configuration)
180
+ self.updater = Updater(self.sdk_configuration)
@@ -0,0 +1,52 @@
1
+ """Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT."""
2
+
3
+
4
+ import requests as requests_http
5
+ from ._hooks import SDKHooks
6
+ from .utils import utils
7
+ from .utils.retries import RetryConfig
8
+ from dataclasses import dataclass, field
9
+ from enum import Enum
10
+ from plex_api.models import components
11
+ from typing import Any, Callable, Dict, List, Optional, Tuple, Union
12
+
13
+
14
+ SERVERS = [
15
+ '{protocol}://{ip}:{port}',
16
+ # The full address of your Plex Server
17
+ ]
18
+ """Contains the list of servers available to the SDK"""
19
+
20
+ class ServerProtocol(str, Enum):
21
+ r"""The protocol to use when connecting to your plex server."""
22
+ HTTP = 'http'
23
+ HTTPS = 'https'
24
+
25
+
26
+ @dataclass
27
+ class SDKConfiguration:
28
+ client: requests_http.Session
29
+ security: Union[components.Security,Callable[[], components.Security]] = None
30
+ server_url: Optional[str] = ''
31
+ server_idx: Optional[int] = 0
32
+ server_defaults: List[Dict[str, str]] = field(default_factory=List)
33
+ globals: Dict[str, Dict[str, Dict[str, Any]]] = field(default_factory=Dict)
34
+ language: str = 'python'
35
+ openapi_doc_version: str = '0.0.3'
36
+ sdk_version: str = '0.6.1'
37
+ gen_version: str = '2.298.2'
38
+ user_agent: str = 'speakeasy-sdk/python 0.6.1 2.298.2 0.0.3 plex-api-client'
39
+ retry_config: Optional[RetryConfig] = None
40
+ _hooks: Optional[SDKHooks] = None
41
+
42
+ def get_server_details(self) -> Tuple[str, Dict[str, str]]:
43
+ if self.server_url is not None and self.server_url != '':
44
+ return utils.remove_suffix(self.server_url, '/'), {}
45
+ if self.server_idx is None:
46
+ self.server_idx = 0
47
+
48
+ return SERVERS[self.server_idx], self.server_defaults[self.server_idx]
49
+
50
+
51
+ def get_hooks(self) -> SDKHooks:
52
+ return self._hooks
plex_api/search.py ADDED
@@ -0,0 +1,232 @@
1
+ """Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT."""
2
+
3
+ import requests as requests_http
4
+ from .sdkconfiguration import SDKConfiguration
5
+ from plex_api import utils
6
+ from plex_api._hooks import AfterErrorContext, AfterSuccessContext, BeforeRequestContext, HookContext
7
+ from plex_api.models import errors, operations
8
+ from typing import Optional
9
+
10
+ class Search:
11
+ r"""API Calls that perform search operations with Plex Media Server"""
12
+ sdk_configuration: SDKConfiguration
13
+
14
+ def __init__(self, sdk_config: SDKConfiguration) -> None:
15
+ self.sdk_configuration = sdk_config
16
+
17
+
18
+
19
+ def perform_search(self, query: str, section_id: Optional[float] = None, limit: Optional[float] = None) -> operations.PerformSearchResponse:
20
+ r"""Perform a search
21
+ This endpoint performs a search across all library sections, or a single section, and returns matches as hubs, split up by type. It performs spell checking, looks for partial matches, and orders the hubs based on quality of results. In addition, based on matches, it will return other related matches (e.g. for a genre match, it may return movies in that genre, or for an actor match, movies with that actor).
22
+
23
+ In the response's items, the following extra attributes are returned to further describe or disambiguate the result:
24
+
25
+ - `reason`: The reason for the result, if not because of a direct search term match; can be either:
26
+ - `section`: There are multiple identical results from different sections.
27
+ - `originalTitle`: There was a search term match from the original title field (sometimes those can be very different or in a foreign language).
28
+ - `<hub identifier>`: If the reason for the result is due to a result in another hub, the source hub identifier is returned. For example, if the search is for \"dylan\" then Bob Dylan may be returned as an artist result, an a few of his albums returned as album results with a reason code of `artist` (the identifier of that particular hub). Or if the search is for \"arnold\", there might be movie results returned with a reason of `actor`
29
+ - `reasonTitle`: The string associated with the reason code. For a section reason, it'll be the section name; For a hub identifier, it'll be a string associated with the match (e.g. `Arnold Schwarzenegger` for movies which were returned because the search was for \"arnold\").
30
+ - `reasonID`: The ID of the item associated with the reason for the result. This might be a section ID, a tag ID, an artist ID, or a show ID.
31
+
32
+ This request is intended to be very fast, and called as the user types.
33
+ """
34
+ hook_ctx = HookContext(operation_id='performSearch', oauth2_scopes=[], security_source=self.sdk_configuration.security)
35
+ request = operations.PerformSearchRequest(
36
+ query=query,
37
+ section_id=section_id,
38
+ limit=limit,
39
+ )
40
+
41
+ base_url = utils.template_url(*self.sdk_configuration.get_server_details())
42
+
43
+ url = base_url + '/hubs/search'
44
+
45
+ if callable(self.sdk_configuration.security):
46
+ headers, query_params = utils.get_security(self.sdk_configuration.security())
47
+ else:
48
+ headers, query_params = utils.get_security(self.sdk_configuration.security)
49
+
50
+ query_params = { **utils.get_query_params(operations.PerformSearchRequest, request, self.sdk_configuration.globals), **query_params }
51
+ headers['Accept'] = 'application/json'
52
+ headers['user-agent'] = self.sdk_configuration.user_agent
53
+ client = self.sdk_configuration.client
54
+
55
+ try:
56
+ req = client.prepare_request(requests_http.Request('GET', url, params=query_params, headers=headers))
57
+ req = self.sdk_configuration.get_hooks().before_request(BeforeRequestContext(hook_ctx), req)
58
+ http_res = client.send(req)
59
+ except Exception as e:
60
+ _, e = self.sdk_configuration.get_hooks().after_error(AfterErrorContext(hook_ctx), None, e)
61
+ if e is not None:
62
+ raise e
63
+
64
+ if utils.match_status_codes(['400','401','4XX','5XX'], http_res.status_code):
65
+ result, e = self.sdk_configuration.get_hooks().after_error(AfterErrorContext(hook_ctx), http_res, None)
66
+ if e is not None:
67
+ raise e
68
+ if result is not None:
69
+ http_res = result
70
+ else:
71
+ http_res = self.sdk_configuration.get_hooks().after_success(AfterSuccessContext(hook_ctx), http_res)
72
+
73
+
74
+
75
+ res = operations.PerformSearchResponse(status_code=http_res.status_code, content_type=http_res.headers.get('Content-Type') or '', raw_response=http_res)
76
+
77
+ if http_res.status_code == 200:
78
+ pass
79
+ elif http_res.status_code == 400 or http_res.status_code >= 400 and http_res.status_code < 500 or http_res.status_code >= 500 and http_res.status_code < 600:
80
+ raise errors.SDKError('API error occurred', http_res.status_code, http_res.text, http_res)
81
+ elif http_res.status_code == 401:
82
+ if utils.match_content_type(http_res.headers.get('Content-Type') or '', 'application/json'):
83
+ out = utils.unmarshal_json(http_res.text, errors.PerformSearchResponseBody)
84
+ out.raw_response = http_res
85
+ raise out
86
+ else:
87
+ content_type = http_res.headers.get('Content-Type')
88
+ raise errors.SDKError(f'unknown content-type received: {content_type}', http_res.status_code, http_res.text, http_res)
89
+ else:
90
+ raise errors.SDKError('unknown status code received', http_res.status_code, http_res.text, http_res)
91
+
92
+ return res
93
+
94
+
95
+
96
+ def perform_voice_search(self, query: str, section_id: Optional[float] = None, limit: Optional[float] = None) -> operations.PerformVoiceSearchResponse:
97
+ r"""Perform a voice search
98
+ This endpoint performs a search specifically tailored towards voice or other imprecise input which may work badly with the substring and spell-checking heuristics used by the `/hubs/search` endpoint.
99
+ It uses a [Levenshtein distance](https://en.wikipedia.org/wiki/Levenshtein_distance) heuristic to search titles, and as such is much slower than the other search endpoint.
100
+ Whenever possible, clients should limit the search to the appropriate type.
101
+ Results, as well as their containing per-type hubs, contain a `distance` attribute which can be used to judge result quality.
102
+ """
103
+ hook_ctx = HookContext(operation_id='performVoiceSearch', oauth2_scopes=[], security_source=self.sdk_configuration.security)
104
+ request = operations.PerformVoiceSearchRequest(
105
+ query=query,
106
+ section_id=section_id,
107
+ limit=limit,
108
+ )
109
+
110
+ base_url = utils.template_url(*self.sdk_configuration.get_server_details())
111
+
112
+ url = base_url + '/hubs/search/voice'
113
+
114
+ if callable(self.sdk_configuration.security):
115
+ headers, query_params = utils.get_security(self.sdk_configuration.security())
116
+ else:
117
+ headers, query_params = utils.get_security(self.sdk_configuration.security)
118
+
119
+ query_params = { **utils.get_query_params(operations.PerformVoiceSearchRequest, request, self.sdk_configuration.globals), **query_params }
120
+ headers['Accept'] = 'application/json'
121
+ headers['user-agent'] = self.sdk_configuration.user_agent
122
+ client = self.sdk_configuration.client
123
+
124
+ try:
125
+ req = client.prepare_request(requests_http.Request('GET', url, params=query_params, headers=headers))
126
+ req = self.sdk_configuration.get_hooks().before_request(BeforeRequestContext(hook_ctx), req)
127
+ http_res = client.send(req)
128
+ except Exception as e:
129
+ _, e = self.sdk_configuration.get_hooks().after_error(AfterErrorContext(hook_ctx), None, e)
130
+ if e is not None:
131
+ raise e
132
+
133
+ if utils.match_status_codes(['400','401','4XX','5XX'], http_res.status_code):
134
+ result, e = self.sdk_configuration.get_hooks().after_error(AfterErrorContext(hook_ctx), http_res, None)
135
+ if e is not None:
136
+ raise e
137
+ if result is not None:
138
+ http_res = result
139
+ else:
140
+ http_res = self.sdk_configuration.get_hooks().after_success(AfterSuccessContext(hook_ctx), http_res)
141
+
142
+
143
+
144
+ res = operations.PerformVoiceSearchResponse(status_code=http_res.status_code, content_type=http_res.headers.get('Content-Type') or '', raw_response=http_res)
145
+
146
+ if http_res.status_code == 200:
147
+ pass
148
+ elif http_res.status_code == 400 or http_res.status_code >= 400 and http_res.status_code < 500 or http_res.status_code >= 500 and http_res.status_code < 600:
149
+ raise errors.SDKError('API error occurred', http_res.status_code, http_res.text, http_res)
150
+ elif http_res.status_code == 401:
151
+ if utils.match_content_type(http_res.headers.get('Content-Type') or '', 'application/json'):
152
+ out = utils.unmarshal_json(http_res.text, errors.PerformVoiceSearchResponseBody)
153
+ out.raw_response = http_res
154
+ raise out
155
+ else:
156
+ content_type = http_res.headers.get('Content-Type')
157
+ raise errors.SDKError(f'unknown content-type received: {content_type}', http_res.status_code, http_res.text, http_res)
158
+ else:
159
+ raise errors.SDKError('unknown status code received', http_res.status_code, http_res.text, http_res)
160
+
161
+ return res
162
+
163
+
164
+
165
+ def get_search_results(self, query: str) -> operations.GetSearchResultsResponse:
166
+ r"""Get Search Results
167
+ This will search the database for the string provided.
168
+ """
169
+ hook_ctx = HookContext(operation_id='getSearchResults', oauth2_scopes=[], security_source=self.sdk_configuration.security)
170
+ request = operations.GetSearchResultsRequest(
171
+ query=query,
172
+ )
173
+
174
+ base_url = utils.template_url(*self.sdk_configuration.get_server_details())
175
+
176
+ url = base_url + '/search'
177
+
178
+ if callable(self.sdk_configuration.security):
179
+ headers, query_params = utils.get_security(self.sdk_configuration.security())
180
+ else:
181
+ headers, query_params = utils.get_security(self.sdk_configuration.security)
182
+
183
+ query_params = { **utils.get_query_params(operations.GetSearchResultsRequest, request, self.sdk_configuration.globals), **query_params }
184
+ headers['Accept'] = 'application/json'
185
+ headers['user-agent'] = self.sdk_configuration.user_agent
186
+ client = self.sdk_configuration.client
187
+
188
+ try:
189
+ req = client.prepare_request(requests_http.Request('GET', url, params=query_params, headers=headers))
190
+ req = self.sdk_configuration.get_hooks().before_request(BeforeRequestContext(hook_ctx), req)
191
+ http_res = client.send(req)
192
+ except Exception as e:
193
+ _, e = self.sdk_configuration.get_hooks().after_error(AfterErrorContext(hook_ctx), None, e)
194
+ if e is not None:
195
+ raise e
196
+
197
+ if utils.match_status_codes(['400','401','4XX','5XX'], http_res.status_code):
198
+ result, e = self.sdk_configuration.get_hooks().after_error(AfterErrorContext(hook_ctx), http_res, None)
199
+ if e is not None:
200
+ raise e
201
+ if result is not None:
202
+ http_res = result
203
+ else:
204
+ http_res = self.sdk_configuration.get_hooks().after_success(AfterSuccessContext(hook_ctx), http_res)
205
+
206
+
207
+
208
+ res = operations.GetSearchResultsResponse(status_code=http_res.status_code, content_type=http_res.headers.get('Content-Type') or '', raw_response=http_res)
209
+
210
+ if http_res.status_code == 200:
211
+ if utils.match_content_type(http_res.headers.get('Content-Type') or '', 'application/json'):
212
+ out = utils.unmarshal_json(http_res.text, Optional[operations.GetSearchResultsResponseBody])
213
+ res.object = out
214
+ else:
215
+ content_type = http_res.headers.get('Content-Type')
216
+ raise errors.SDKError(f'unknown content-type received: {content_type}', http_res.status_code, http_res.text, http_res)
217
+ elif http_res.status_code == 400 or http_res.status_code >= 400 and http_res.status_code < 500 or http_res.status_code >= 500 and http_res.status_code < 600:
218
+ raise errors.SDKError('API error occurred', http_res.status_code, http_res.text, http_res)
219
+ elif http_res.status_code == 401:
220
+ if utils.match_content_type(http_res.headers.get('Content-Type') or '', 'application/json'):
221
+ out = utils.unmarshal_json(http_res.text, errors.GetSearchResultsResponseBody)
222
+ out.raw_response = http_res
223
+ raise out
224
+ else:
225
+ content_type = http_res.headers.get('Content-Type')
226
+ raise errors.SDKError(f'unknown content-type received: {content_type}', http_res.status_code, http_res.text, http_res)
227
+ else:
228
+ raise errors.SDKError('unknown status code received', http_res.status_code, http_res.text, http_res)
229
+
230
+ return res
231
+
232
+