diff options
Diffstat (limited to 'server/src/routes/ui/assets.rs')
-rw-r--r-- | server/src/routes/ui/assets.rs | 200 |
1 files changed, 0 insertions, 200 deletions
diff --git a/server/src/routes/ui/assets.rs b/server/src/routes/ui/assets.rs deleted file mode 100644 index c661771..0000000 --- a/server/src/routes/ui/assets.rs +++ /dev/null @@ -1,200 +0,0 @@ -/* - 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) 2025 metamuffin <metamuffin.org> -*/ -use crate::routes::ui::{account::session::Session, error::MyResult, CacheControlFile}; -use anyhow::{anyhow, bail, Context}; -use base64::Engine; -use jellybase::{ - assetfed::AssetInner, cache::async_cache_file, database::Database, federation::Federation, CONF, -}; -use jellycommon::{LocalTrack, NodeID, PeopleGroup, SourceTrackKind, TrackSource}; -use log::info; -use rocket::{get, http::ContentType, response::Redirect, State}; -use std::{path::PathBuf, str::FromStr}; - -pub const AVIF_QUALITY: f32 = 50.; -pub const AVIF_SPEED: u8 = 5; - -#[get("/asset/<token>?<width>")] -pub async fn r_asset( - _session: Session, - fed: &State<Federation>, - token: &str, - width: Option<usize>, -) -> MyResult<(ContentType, CacheControlFile)> { - let width = width.unwrap_or(2048); - let asset = AssetInner::deser(token)?; - - let path = if let AssetInner::Federated { host, asset } = asset { - let session = fed.get_session(&host).await?; - - let asset = base64::engine::general_purpose::URL_SAFE.encode(asset); - async_cache_file("fed-asset", &asset, |out| async { - session.asset(out, &asset, width).await - }) - .await? - } else { - let source = resolve_asset(asset).await.context("resolving asset")?; - - // fit the resolution into a finite set so the maximum cache is finite too. - let width = 2usize.pow(width.clamp(128, 2048).ilog2()); - jellytranscoder::image::transcode(&source, AVIF_QUALITY, AVIF_SPEED, width) - .await - .context("transcoding asset")? - }; - info!("loading asset from {path:?}"); - Ok(( - ContentType::AVIF, - CacheControlFile::new_cachekey(&path.abs()).await?, - )) -} - -pub async fn resolve_asset(asset: AssetInner) -> anyhow::Result<PathBuf> { - match asset { - AssetInner::Cache(c) => Ok(c.abs()), - AssetInner::Assets(c) => Ok(CONF.asset_path.join(c)), - AssetInner::Media(c) => Ok(CONF.media_path.join(c)), - _ => bail!("wrong asset type"), - } -} - -#[get("/n/<id>/poster?<width>")] -pub async fn r_item_poster( - _session: Session, - db: &State<Database>, - id: NodeID, - width: Option<usize>, -) -> MyResult<Redirect> { - // TODO perm - let node = db.get_node(id)?.ok_or(anyhow!("node does not exist"))?; - - let mut asset = node.poster.clone(); - if asset.is_none() { - if let Some(parent) = node.parents.last().copied() { - let parent = db.get_node(parent)?.ok_or(anyhow!("node does not exist"))?; - asset = parent.poster.clone(); - } - }; - let asset = asset.unwrap_or_else(|| { - AssetInner::Assets(format!("fallback-{:?}.avif", node.kind).into()).ser() - }); - Ok(Redirect::permanent(rocket::uri!(r_asset(asset.0, width)))) -} - -#[get("/n/<id>/backdrop?<width>")] -pub async fn r_item_backdrop( - _session: Session, - db: &State<Database>, - id: NodeID, - width: Option<usize>, -) -> MyResult<Redirect> { - // TODO perm - let node = db.get_node(id)?.ok_or(anyhow!("node does not exist"))?; - - let mut asset = node.backdrop.clone(); - if asset.is_none() { - if let Some(parent) = node.parents.last().copied() { - let parent = db.get_node(parent)?.ok_or(anyhow!("node does not exist"))?; - asset = parent.backdrop.clone(); - } - }; - let asset = asset.unwrap_or_else(|| { - AssetInner::Assets(format!("fallback-{:?}.avif", node.kind).into()).ser() - }); - Ok(Redirect::permanent(rocket::uri!(r_asset(asset.0, width)))) -} - -#[get("/n/<id>/person/<index>/asset?<group>&<width>")] -pub async fn r_person_asset( - _session: Session, - db: &State<Database>, - id: NodeID, - index: usize, - group: String, - width: Option<usize>, -) -> MyResult<Redirect> { - // TODO perm - - let node = db.get_node(id)?.ok_or(anyhow!("node does not exist"))?; - let app = node - .people - .get(&PeopleGroup::from_str(&group).map_err(|()| anyhow!("unknown people group"))?) - .ok_or(anyhow!("group has no members"))? - .get(index) - .ok_or(anyhow!("person does not exist"))?; - - let asset = app - .person - .headshot - .to_owned() - .unwrap_or(AssetInner::Assets("fallback-Person.avif".into()).ser()); - Ok(Redirect::permanent(rocket::uri!(r_asset(asset.0, width)))) -} - -// TODO this can create "federation recursion" because track selection cannot be relied on. -//? TODO is this still relevant? - -#[get("/n/<id>/thumbnail?<t>&<width>")] -pub async fn r_node_thumbnail( - _session: Session, - db: &State<Database>, - fed: &State<Federation>, - id: NodeID, - t: f64, - width: Option<usize>, -) -> MyResult<Redirect> { - let node = db.get_node(id)?.ok_or(anyhow!("node does not exist"))?; - - let media = node.media.as_ref().ok_or(anyhow!("no media"))?; - let (thumb_track_index, thumb_track) = media - .tracks - .iter() - .enumerate() - .find(|(_i, t)| matches!(t.kind, SourceTrackKind::Video { .. })) - .ok_or(anyhow!("no video track to create a thumbnail of"))?; - let source = media - .tracks - .get(thumb_track_index) - .ok_or(anyhow!("no source"))?; - let thumb_track_source = source.source.clone(); - - if t < 0. || t > media.duration { - Err(anyhow!("thumbnail instant not within media duration"))? - } - - let step = 8.; - let t = (t / step).floor() * step; - - let asset = match thumb_track_source { - TrackSource::Local(a) => { - let AssetInner::LocalTrack(LocalTrack { path, .. }) = AssetInner::deser(&a.0)? else { - return Err(anyhow!("track set to wrong asset type").into()); - }; - // the track selected might be different from thumb_track - jellytranscoder::thumbnail::create_thumbnail(&CONF.media_path.join(path), t).await? - } - TrackSource::Remote(_) => { - // TODO in the new system this is preferrably a property of node ext for regular fed - let session = fed - .get_session( - thumb_track - .federated - .last() - .ok_or(anyhow!("federation broken"))?, - ) - .await?; - - async_cache_file("fed-thumb", (id, t as i64), |out| { - session.node_thumbnail(out, id.into(), 2048, t) - }) - .await? - } - }; - - Ok(Redirect::temporary(rocket::uri!(r_asset( - AssetInner::Cache(asset).ser().0, - width - )))) -} |