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.rs115
1 files changed, 75 insertions, 40 deletions
diff --git a/server/src/routes/ui/assets.rs b/server/src/routes/ui/assets.rs
index 7973b0c..97396f3 100644
--- a/server/src/routes/ui/assets.rs
+++ b/server/src/routes/ui/assets.rs
@@ -3,22 +3,62 @@
which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
Copyright (C) 2023 metamuffin <metamuffin.org>
*/
-use crate::{
- database::DataAcid,
- routes::ui::{account::session::Session, error::MyResult, CacheControlFile},
-};
+use crate::routes::ui::{account::session::Session, error::MyResult, CacheControlFile};
use anyhow::{anyhow, Context};
+use base64::Engine;
use jellybase::{
+ assetfed::AssetInner,
cache::async_cache_file,
- database::{TableExt, T_NODE, T_NODE_EXTENDED},
+ database::{DataAcid, TableExt, T_NODE, T_NODE_EXTENDED},
federation::Federation,
permission::NodePermissionExt,
- AssetLocationExt,
+ CONF,
};
pub use jellycommon::AssetRole;
-use jellycommon::{AssetLocation, LocalTrack, PeopleGroup, SourceTrackKind, TrackSource};
+use jellycommon::{LocalTrack, PeopleGroup, SourceTrackKind, TrackSource};
use log::info;
-use rocket::{get, http::ContentType, State};
+use rocket::{get, http::ContentType, response::Redirect, State};
+use std::path::PathBuf;
+
+#[get("/asset/<token>?<width>")]
+pub async fn r_asset(
+ _session: Session,
+ fed: &State<Federation>,
+ token: &str,
+ width: Option<usize>,
+) -> MyResult<(ContentType, CacheControlFile)> {
+ let asset = AssetInner::deser(token)?;
+
+ let source = resolve_asset(asset, fed).await.context("resolving asset")?;
+
+ // fit the resolution into a finite set so the maximum cache is finite too.
+ let width = 2usize.pow(width.unwrap_or(2048).clamp(128, 2048).ilog2());
+ let path = jellytranscoder::image::transcode(source, 50., 5, 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, fed: &State<Federation>) -> anyhow::Result<PathBuf> {
+ match asset {
+ AssetInner::Federated { host, asset } => {
+ let session = fed.get_session(&host).await?;
+
+ let asset = base64::engine::general_purpose::URL_SAFE.encode(asset);
+ Ok(async_cache_file(&["fed-asset", &asset], |out| async {
+ session.asset(out, &asset, 2048).await
+ })
+ .await?
+ .abs())
+ }
+ AssetInner::Cache(c) => Ok(c.abs()),
+ AssetInner::Assets(c) => Ok(CONF.asset_path.join(c)),
+ }
+}
#[get("/n/<id>/asset?<role>&<width>")]
pub async fn r_item_assets(
@@ -27,15 +67,15 @@ pub async fn r_item_assets(
id: &str,
role: AssetRole,
width: Option<usize>,
-) -> MyResult<(ContentType, CacheControlFile)> {
+) -> MyResult<Redirect> {
let node = T_NODE
.get(&db, id)?
.only_if_permitted(&session.user.permissions)
.ok_or(anyhow!("node does not exist"))?;
let mut asset = match role {
- AssetRole::Backdrop => node.private.backdrop,
- AssetRole::Poster => node.private.poster,
+ AssetRole::Backdrop => node.public.backdrop,
+ AssetRole::Poster => node.public.poster,
};
if let None = asset {
if let Some(parent) = &node.public.path.last() {
@@ -43,15 +83,18 @@ pub async fn r_item_assets(
.get(&db, parent.as_str())?
.ok_or(anyhow!("node does not exist"))?;
asset = match role {
- AssetRole::Backdrop => parent.private.backdrop,
- AssetRole::Poster => parent.private.poster,
+ AssetRole::Backdrop => parent.public.backdrop,
+ AssetRole::Poster => parent.public.poster,
};
}
};
- let asset = asset.unwrap_or(AssetLocation::Assets(
- format!("fallback-{:?}.avif", node.public.kind.unwrap_or_default()).into(),
- ));
- Ok(asset_with_res(asset, width).await?)
+ let asset = asset.unwrap_or_else(|| {
+ AssetInner::Assets(
+ format!("fallback-{:?}.avif", node.public.kind.unwrap_or_default()).into(),
+ )
+ .ser()
+ });
+ Ok(Redirect::temporary(rocket::uri!(r_asset(asset.0, width))))
}
#[get("/n/<id>/person/<index>/asset?<group>&<width>")]
@@ -62,7 +105,7 @@ pub async fn r_person_asset(
index: usize,
group: PeopleGroup,
width: Option<usize>,
-) -> MyResult<(ContentType, CacheControlFile)> {
+) -> MyResult<Redirect> {
T_NODE
.get(&db, id)?
.only_if_permitted(&session.user.permissions)
@@ -76,13 +119,17 @@ pub async fn r_person_asset(
.get(index)
.ok_or(anyhow!("person does not exist"))?;
- let asset = app.person.asset.to_owned().unwrap_or(AssetLocation::Assets(
- format!("fallback-Person.avif").into(),
- ));
- Ok(asset_with_res(asset, width).await?)
+ let asset = app
+ .person
+ .headshot
+ .to_owned()
+ .unwrap_or(AssetInner::Assets(format!("fallback-Person.avif").into()).ser());
+ Ok(Redirect::temporary(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,
@@ -91,7 +138,7 @@ pub async fn r_node_thumbnail(
id: &str,
t: f64,
width: Option<usize>,
-) -> MyResult<(ContentType, CacheControlFile)> {
+) -> MyResult<Redirect> {
let node = T_NODE
.get(&db, id)?
.only_if_permitted(&session.user.permissions)
@@ -120,6 +167,7 @@ pub async fn r_node_thumbnail(
jellytranscoder::thumbnail::create_thumbnail(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
@@ -136,21 +184,8 @@ pub async fn r_node_thumbnail(
}
};
- Ok(asset_with_res(asset, width).await?)
-}
-
-async fn asset_with_res(
- asset: AssetLocation,
- width: Option<usize>,
-) -> MyResult<(ContentType, CacheControlFile)> {
- // fit the resolution into a finite set so the maximum cache is finite too.
- let width = 2usize.pow(width.unwrap_or(2048).clamp(128, 2048).ilog2());
- let path = jellytranscoder::image::transcode(asset, 50., 5, width)
- .await
- .context("transcoding asset")?;
- info!("loading asset from {path:?}");
- Ok((
- ContentType::AVIF,
- CacheControlFile::new_cachekey(&path.path()).await?,
- ))
+ Ok(Redirect::temporary(rocket::uri!(r_asset(
+ AssetInner::Cache(asset).ser().0,
+ width
+ ))))
}