aboutsummaryrefslogtreecommitdiff
path: root/server/src/request_info.rs
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2026-01-24 04:31:48 +0100
committermetamuffin <metamuffin@disroot.org>2026-01-24 04:31:48 +0100
commitb2e88a8beabf04adc28947cf82996e8692a68b71 (patch)
tree23d66c8672b69cce7835ffabae4092669062ada8 /server/src/request_info.rs
parent774f64c0789529884dd7a5232f190e347ad29532 (diff)
downloadjellything-b2e88a8beabf04adc28947cf82996e8692a68b71.tar
jellything-b2e88a8beabf04adc28947cf82996e8692a68b71.tar.bz2
jellything-b2e88a8beabf04adc28947cf82996e8692a68b71.tar.zst
move things around; kv crate
Diffstat (limited to 'server/src/request_info.rs')
-rw-r--r--server/src/request_info.rs128
1 files changed, 128 insertions, 0 deletions
diff --git a/server/src/request_info.rs b/server/src/request_info.rs
new file mode 100644
index 0000000..779b4e1
--- /dev/null
+++ b/server/src/request_info.rs
@@ -0,0 +1,128 @@
+/*
+ This file is part of jellything (https://codeberg.org/metamuffin/jellything)
+ which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
+ Copyright (C) 2026 metamuffin <metamuffin.org>
+*/
+
+use crate::ui::error::{MyError, MyResult};
+use anyhow::anyhow;
+use jellycommon::jellyobject::ObjectBuffer;
+use jellyui::RenderInfo;
+use rocket::{
+ Request, async_trait,
+ http::{MediaType, Status},
+ request::{FromRequest, Outcome},
+};
+
+pub struct RequestInfo<'a> {
+ pub lang: &'a str,
+ pub accept: Accept,
+ pub user: Option<ObjectBuffer>,
+}
+
+#[async_trait]
+impl<'r> FromRequest<'r> for RequestInfo<'r> {
+ type Error = MyError;
+ async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
+ match Self::from_request_ut(request).await {
+ Ok(a) => Outcome::Success(a),
+ Err(a) => Outcome::Error((Status::BadRequest, a)),
+ }
+ }
+}
+
+impl<'a> RequestInfo<'a> {
+ pub async fn from_request_ut(request: &'a Request<'_>) -> MyResult<Self> {
+ Ok(Self {
+ lang: accept_language(request),
+ accept: Accept::from_request_ut(request),
+ user: None,
+ // session: session_from_request(request).await?,
+ })
+ }
+ pub fn render_info(&self) -> RenderInfo<'a> {
+ RenderInfo {
+ lang: self.lang,
+ status_message: None,
+ user: self.user.as_ref().map(|u| u.as_object()),
+ config: CONF.ui,
+ }
+ }
+}
+
+#[derive(Debug, Default)]
+pub enum Accept {
+ #[default]
+ Other,
+ Json,
+ Image,
+ Media,
+ Html,
+}
+impl Accept {
+ pub fn from_request_ut(request: &Request) -> Self {
+ if let Some(a) = request.accept() {
+ if a.preferred().exact_eq(&MediaType::JSON) {
+ Accept::Json
+ } else {
+ Accept::Other
+ }
+ } else {
+ Accept::Other
+ }
+ }
+
+ pub fn is_json(&self) -> bool {
+ matches!(self, Self::Json)
+ }
+}
+
+pub(super) fn accept_language<'a>(request: &'a Request<'_>) -> &'a str {
+ request
+ .headers()
+ .get_one("accept-language")
+ .and_then(|h| {
+ h.split(",")
+ .filter_map(|e| {
+ let code = e.split(";").next()?;
+ let code = code.split_once("-").unwrap_or((code, "")).0;
+ Some(code)
+ })
+ .next()
+ })
+ .unwrap_or("en")
+}
+
+pub(super) async fn session_from_request(req: &Request<'_>) -> Result<Session, MyError> {
+ if cfg!(feature = "bypass-auth") {
+ Ok(bypass_auth_session()?)
+ } else {
+ let token = req
+ .query_value("session")
+ .map(|e| e.unwrap())
+ .or_else(|| req.query_value("api_key").map(|e| e.unwrap()))
+ .or_else(|| req.headers().get_one("X-MediaBrowser-Token"))
+ .or_else(|| {
+ req.headers()
+ .get_one("Authorization")
+ .and_then(parse_jellyfin_auth)
+ }) // for jellyfin compat
+ .or(req.cookies().get("session").map(|cookie| cookie.value()))
+ .ok_or(anyhow!("not logged in"))?;
+
+ // jellyfin urlescapes the token for *some* requests
+ let token = token.replace("%3D", "=");
+ Ok(token_to_session(&token)?)
+ }
+}
+
+fn parse_jellyfin_auth(h: &str) -> Option<&str> {
+ for tok in h.split(" ") {
+ if let Some(tok) = tok.strip_prefix("Token=\"")
+ && let Some(tok) = tok.strip_suffix("\"")
+ {
+ return Some(tok);
+ }
+ }
+ None
+}