aboutsummaryrefslogtreecommitdiff
path: root/server/src/routes/ui/assets.rs
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/routes/ui/assets.rs')
-rw-r--r--server/src/routes/ui/assets.rs200
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
- ))))
-}