diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/Cargo.toml | 1 | ||||
-rw-r--r-- | server/src/import.rs | 48 | ||||
-rw-r--r-- | server/src/routes/ui/assets.rs | 23 |
3 files changed, 66 insertions, 6 deletions
diff --git a/server/Cargo.toml b/server/Cargo.toml index 6118240..6648b80 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -22,6 +22,7 @@ base64 = "0.21.2" chrono = { version = "0.4.26", features = ["serde"] } argon2 = "0.5.1" +sha2 = "0.10.7" aes-gcm-siv = "0.11.1" async-std = "1.12.0" diff --git a/server/src/import.rs b/server/src/import.rs index 2cd1998..a059694 100644 --- a/server/src/import.rs +++ b/server/src/import.rs @@ -6,10 +6,13 @@ use crate::{database::Database, federation::Federation, CONF}; use anyhow::{anyhow, bail, Context, Ok}; use async_recursion::async_recursion; +use base64::Engine; use jellyclient::Session; -use jellycommon::{MediaSource, Node, NodePrivate, RemoteImportOptions}; +use jellycommon::{AssetLocation, MediaSource, Node, NodePrivate, RemoteImportOptions}; use log::{error, info}; -use std::{ffi::OsStr, fs::File, os::unix::prelude::OsStrExt, path::PathBuf, sync::LazyLock}; +use std::{ + ffi::OsStr, fs::File, os::unix::prelude::OsStrExt, path::PathBuf, str::FromStr, sync::LazyLock, +}; use tokio::sync::Semaphore; static IMPORT_SEM: LazyLock<Semaphore> = LazyLock::new(|| Semaphore::new(1)); @@ -125,11 +128,15 @@ async fn import_remote( .node(&opts.id) .await .context("fetching remote node")?; + + let poster = cache_federation_asset(session, &opts.id, "poster").await?; + let backdrop = cache_federation_asset(session, &opts.id, "backdrop").await?; + let mut node = Node { public: node, private: NodePrivate { - backdrop: None, - poster: None, + backdrop: Some(AssetLocation::Cache(backdrop)), + poster: Some(AssetLocation::Cache(poster)), import: None, source: Some(MediaSource::Remote { host: opts.host.clone(), @@ -160,3 +167,36 @@ async fn import_remote( Ok(()) } + +async fn cache_federation_asset( + session: &Session, + identifier: &String, + role: &str, +) -> anyhow::Result<PathBuf> { + let (poster, download) = cache_file(&["federation-asset", role, identifier]); + if let Some(d) = download { + session.node_asset(&identifier, role, d).await?; + } + Ok(poster) +} + +fn cache_file(seed: &[&str]) -> (PathBuf, Option<File>) { + use sha2::Digest; + let mut d = sha2::Sha512::new(); + for s in seed { + d.update(s.as_bytes()); + d.update(b"\0"); + } + let d = d.finalize(); + let fname = base64::engine::general_purpose::URL_SAFE.encode(d); + let fname = &fname[..22]; // about 128 bits + let fullpath = CONF.cache_path.join(fname); + let cachepath = PathBuf::from_str(fname).unwrap(); + + let f = if !fullpath.exists() { + Some(File::create(&fullpath).unwrap()) + } else { + None + }; + (cachepath, f) +} diff --git a/server/src/routes/ui/assets.rs b/server/src/routes/ui/assets.rs index 7fa083c..4b5c64a 100644 --- a/server/src/routes/ui/assets.rs +++ b/server/src/routes/ui/assets.rs @@ -9,6 +9,7 @@ use crate::{ CONF, }; use anyhow::anyhow; +use jellycommon::AssetLocation; use log::info; use rocket::{get, http::ContentType, FromFormField, State, UriDisplayQuery}; use std::{path::PathBuf, str::FromStr}; @@ -16,7 +17,9 @@ use tokio::fs::File; #[derive(FromFormField, UriDisplayQuery)] pub enum AssetRole { + #[field(value = "poster")] Poster, + #[field(value = "backdrop")] Backdrop, } @@ -32,15 +35,31 @@ pub async fn r_item_assets( AssetRole::Backdrop => node.private.backdrop, AssetRole::Poster => node.private.poster, } - .map(|e| CONF.library_path.join(e)) + .map(|e| e.path()) .unwrap_or_else(|| { CONF.asset_path .join(PathBuf::from_str("fallback.jpeg").unwrap()) }); info!("loading asset from {path:?}"); - let ext = path.extension().unwrap().to_str().unwrap(); + let ext = path + .extension() + .map(|e| e.to_str().unwrap()) + .unwrap_or("jpeg"); Ok(( ContentType::from_extension(ext).unwrap(), CacheControlFile::new(File::open(path).await?).await, )) } + +pub trait AssetLocationExt { + fn path(&self) -> PathBuf; +} +impl AssetLocationExt for AssetLocation { + fn path(&self) -> PathBuf { + match self { + AssetLocation::Assets(p) => CONF.asset_path.join(p), + AssetLocation::Cache(p) => CONF.cache_path.join(p), + AssetLocation::Library(p) => CONF.library_path.join(p), + } + } +} |