1use crate::media_container::server::Feature;
2use isahc::{AsyncBody, AsyncReadResponseExt, Response as HttpResponse};
3use serde::Deserialize;
4use thiserror::Error;
5
6#[derive(Debug, Error)]
7pub enum Error {
8 #[error("Authenticated client must be provided.")]
9 ClientNotAuthenticated,
10 #[error("Non-authenticated client must be provided.")]
11 ClientAuthenticated,
12 #[error("Unable to deserialize JSON: {source}.")]
13 JsonDeserealiseError {
14 #[from]
15 source: serde_json::Error,
16 },
17 #[error("Unable to deserialize XML: {source}.")]
18 XmlDeserealiseError {
19 #[from]
20 source: quick_xml::de::DeError,
21 },
22 #[error("{source}")]
23 UrlencodingError {
24 #[from]
25 source: serde_urlencoded::ser::Error,
26 },
27 #[error("{source}")]
28 HttpError {
29 #[from]
30 source: http::Error,
31 },
32 #[error("{source}")]
33 IsahcHttpError {
34 #[from]
35 source: isahc::http::Error,
36 },
37 #[error("{source}")]
38 IsahcError {
39 #[from]
40 source: isahc::Error,
41 },
42 #[error("{source}")]
43 StdIoError {
44 #[from]
45 source: std::io::Error,
46 },
47 #[error("Error while communicating with MyPlexApi: {errors:?}.")]
48 MyPlexErrorResponse { errors: Vec<Self> },
49 #[error("Error occurred while communicating to MyPlex API: #{code} - {message}.")]
50 MyPlexApiError { code: i32, message: String },
51 #[error("Failed to get claim token: {0}.")]
52 FailedToGetClaimToken(String),
53 #[error("Unexpected API response: HTTP {status_code}, content: {content}.")]
54 UnexpectedApiResponse { status_code: u16, content: String },
55 #[error("The requested webhook wasn't found: {0}.")]
56 WebhookNotFound(String),
57 #[error("The mandatory feature is not available: {0}.")]
58 SubscriptionFeatureNotAvailable(Feature),
59 #[error("OTP is required for the authentication.")]
60 OtpRequired,
61 #[error("OTP is provided, but no username/password.")]
62 UselessOtp,
63 #[error("Connecting to the device is not supported.")]
64 DeviceConnectionNotSupported,
65 #[error("Device doesn't have any exposed connection endpoints.")]
66 DeviceConnectionsIsEmpty,
67 #[error("Requested unknown setting: {0}.")]
68 RequestedSettingNotFound(String),
69 #[error("You can't set setting to a value of a different type.")]
70 IncompatibleSettingValues,
71 #[error("Provided pin is already expired.")]
72 PinExpired,
73 #[error("Provided pin is not linked yet.")]
74 PinNotLinked,
75 #[error("Item requested was not found on the server.")]
76 ItemNotFound,
77 #[error("The requested transcode parameters were invalid.")]
78 InvalidTranscodeSettings,
79 #[error("The transcode request failed: {0}.")]
80 TranscodeError(String),
81 #[error("The server thinks the client should just play the original media.")]
82 TranscodeRefused,
83 #[error("Transcoding is incomplete.")]
84 TranscodeIncomplete,
85 #[error("Invalid header value.")]
86 InvalidHeaderValue,
87 #[error("Unknown container format.")]
88 UnknownContainerFormat(String),
89 #[error("Only invites with status pending_received can be accepted.")]
90 InviteAcceptingNotPendingReceived,
91 #[error("Unexpected error. Please create a bug report.")]
92 UnexpectedError,
93}
94
95const PLEX_API_ERROR_CODE_AUTH_OTP_REQUIRED: i32 = 1029;
96
97impl Error {
98 pub async fn from_response(mut response: HttpResponse<AsyncBody>) -> Self {
99 let status_code = response.status().as_u16();
100 let response_body = match response.text().await {
101 Ok(body) => body,
102 Err(err) => {
103 return err.into();
104 }
105 };
106
107 let err: Result<MyPlexApiErrorResponse, Error>;
108 if let Some(content_type) = response.headers().get("Content-type") {
109 match content_type.to_str().unwrap().split("; ").next().unwrap() {
110 "application/xml" => {
111 err = quick_xml::de::from_str::<MyPlexApiErrorResponse>(&response_body)
112 .map_err(|e| e.into())
113 }
114 _ => {
115 err = serde_json::from_str::<MyPlexApiErrorResponse>(&response_body)
116 .map_err(|e| e.into())
117 }
118 }
119 } else {
120 err = serde_json::from_str::<MyPlexApiErrorResponse>(&response_body)
121 .map_err(|e| e.into());
122 }
123
124 match err {
125 Ok(r) => {
126 if r.errors.len() == 1 && r.errors[0].code == PLEX_API_ERROR_CODE_AUTH_OTP_REQUIRED
127 {
128 Self::OtpRequired
129 } else {
130 r.into()
131 }
132 }
133 Err(_) => Self::UnexpectedApiResponse {
134 status_code,
135 content: response_body,
136 },
137 }
138 }
139}
140
141#[derive(Deserialize, Debug, Clone)]
142#[cfg_attr(feature = "tests_deny_unknown_fields", serde(deny_unknown_fields))]
143struct MyPlexApiError {
144 code: i32,
145 message: String,
146
147 #[allow(dead_code)]
149 status: u16,
150}
151
152#[derive(Deserialize, Debug, Clone)]
153#[cfg_attr(feature = "tests_deny_unknown_fields", serde(deny_unknown_fields))]
154pub(crate) struct MyPlexApiErrorResponse {
155 errors: Vec<MyPlexApiError>,
156}
157
158impl From<MyPlexApiError> for Error {
159 fn from(error: MyPlexApiError) -> Self {
160 Self::MyPlexApiError {
161 code: error.code,
162 message: error.message,
163 }
164 }
165}
166
167impl From<MyPlexApiErrorResponse> for Error {
168 fn from(r: MyPlexApiErrorResponse) -> Self {
169 Self::MyPlexErrorResponse {
170 errors: r.errors.into_iter().map(|e| e.into()).collect(),
171 }
172 }
173}