diff options
| author | metamuffin <metamuffin@disroot.org> | 2026-01-24 04:31:48 +0100 |
|---|---|---|
| committer | metamuffin <metamuffin@disroot.org> | 2026-01-24 04:31:48 +0100 |
| commit | b2e88a8beabf04adc28947cf82996e8692a68b71 (patch) | |
| tree | 23d66c8672b69cce7835ffabae4092669062ada8 /server/src/request_info.rs | |
| parent | 774f64c0789529884dd7a5232f190e347ad29532 (diff) | |
| download | jellything-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.rs | 128 |
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 +} |