aboutsummaryrefslogtreecommitdiff
path: root/server/src
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2024-01-24 18:11:23 +0100
committermetamuffin <metamuffin@disroot.org>2024-01-24 18:47:29 +0100
commit7323709537c6ff14136cd79fb07606cd79391758 (patch)
tree3d817d449d4c0a821b9b5073c8acf826c6ccfda1 /server/src
parentcbb2e163abfefd8ed61c41a096d5d6c27b4721b4 (diff)
downloadjellything-7323709537c6ff14136cd79fb07606cd79391758.tar
jellything-7323709537c6ff14136cd79fb07606cd79391758.tar.bz2
jellything-7323709537c6ff14136cd79fb07606cd79391758.tar.zst
refactor asset system pt. 1
Diffstat (limited to 'server/src')
-rw-r--r--server/src/routes/mod.rs3
-rw-r--r--server/src/routes/ui/assets.rs115
-rw-r--r--server/src/routes/ui/error.rs7
3 files changed, 80 insertions, 45 deletions
diff --git a/server/src/routes/mod.rs b/server/src/routes/mod.rs
index ca8c009..dec0858 100644
--- a/server/src/routes/mod.rs
+++ b/server/src/routes/mod.rs
@@ -33,7 +33,7 @@ use ui::{
r_admin_remove_invite,
user::{r_admin_remove_user, r_admin_user, r_admin_user_permission, r_admin_users},
},
- assets::{r_item_assets, r_node_thumbnail, r_person_asset},
+ assets::{r_asset, r_item_assets, r_node_thumbnail, r_person_asset},
browser::r_all_items_filter,
error::{r_api_catch, r_catch},
home::{r_home, r_home_unpriv},
@@ -98,6 +98,7 @@ pub fn build_rocket(database: DataAcid, federation: Federation) -> Rocket<Build>
r_home_unpriv,
r_streamsync,
r_favicon,
+ r_asset,
r_item_assets,
r_person_asset,
r_search,
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
+ ))))
}
diff --git a/server/src/routes/ui/error.rs b/server/src/routes/ui/error.rs
index d111041..8154209 100644
--- a/server/src/routes/ui/error.rs
+++ b/server/src/routes/ui/error.rs
@@ -5,8 +5,7 @@
*/
use super::layout::{DynLayoutPage, LayoutPage};
use crate::{routes::ui::account::rocket_uri_macro_r_account_login, uri};
-use jellybase::AssetLocationExt;
-use jellycommon::AssetLocation;
+use jellybase::CONF;
use log::info;
use rocket::{
catch,
@@ -15,11 +14,11 @@ use rocket::{
Request,
};
use serde_json::{json, Value};
-use std::{fmt::Display, fs::File, io::Read, path::PathBuf, str::FromStr, sync::LazyLock};
+use std::{fmt::Display, fs::File, io::Read, sync::LazyLock};
static ERROR_IMAGE: LazyLock<Vec<u8>> = LazyLock::new(|| {
info!("loading error image");
- let mut f = File::open(AssetLocation::Assets(PathBuf::from_str("error.avif").unwrap()).path())
+ let mut f = File::open(CONF.asset_path.join("error.avif"))
.expect("please create error.avif in the asset dir");
let mut o = Vec::new();
f.read_to_end(&mut o).unwrap();