diff --git a/src/details.rs b/src/details.rs index 2595fd5..349b99d 100644 --- a/src/details.rs +++ b/src/details.rs @@ -25,6 +25,7 @@ enum ApiFormat { #[derive(Debug, serde::Serialize)] pub(crate) struct ApiDetails { + blurhash: String, width: u16, height: u16, frames: Option, @@ -49,7 +50,7 @@ pub(crate) struct DetailsInner { } impl Details { - pub(crate) fn into_api_details(self) -> ApiDetails { + pub(crate) fn into_api_details(self, blurhash: String) -> ApiDetails { let Details { inner: DetailsInner { @@ -63,6 +64,7 @@ impl Details { } = self; ApiDetails { + blurhash, width, height, frames, diff --git a/src/lib.rs b/src/lib.rs index cf25314..5450836 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -357,12 +357,20 @@ async fn handle_upload( tracing::debug!("Uploaded {} as {:?}", image.filename, alias); let delete_token = image.result.delete_token(); - let details = ensure_details(&state, alias).await?; + let hash = state + .repo + .hash(alias) + .await? + .ok_or(UploadError::MissingAlias)?; + + let details = ensure_details_hash(&state, hash.clone()).await?; + + let blurhash = ensure_blurhash(&state, hash, Some(&details)).await?; files.push(serde_json::json!({ "file": alias.to_string(), "delete_token": delete_token.to_string(), - "details": details.into_api_details(), + "details": details.into_api_details(blurhash.to_string()), })); } } @@ -505,14 +513,20 @@ async fn claim_upload( match upload_result { UploadResult::Success { alias, token } => { - let details = ensure_details(&state, &alias).await?; + let hash = state + .repo + .hash(&alias) + .await? + .ok_or(UploadError::MissingAlias)?; + let details = ensure_details_hash(&state, hash.clone()).await?; + let blurhash = ensure_blurhash(&state, hash, Some(&details)).await?; Ok(HttpResponse::Ok().json(serde_json::json!({ "msg": "ok", "files": [{ "file": alias.to_string(), "delete_token": token.to_string(), - "details": details.into_api_details(), + "details": details.into_api_details(blurhash.to_string()), }] }))) } @@ -612,12 +626,19 @@ async fn do_download_inline( let (alias, delete_token, details) = ingest_inline(stream, state, upload_query).await?; + let hash = state + .repo + .hash(&alias) + .await? + .ok_or(UploadError::MissingAlias)?; + let blurhash = ensure_blurhash(state, hash, Some(&details)).await?; + Ok(HttpResponse::Created().json(serde_json::json!({ "msg": "ok", "files": [{ "file": alias.to_string(), "delete_token": delete_token.to_string(), - "details": details.into_api_details(), + "details": details.into_api_details(blurhash.to_string()), }] }))) } @@ -679,7 +700,7 @@ struct HashJson { /// Get a page of hashes #[tracing::instrument(name = "Hash Page", skip(state))] -async fn page( +async fn page( state: web::Data>, web::Query(PageQuery { slug, @@ -712,11 +733,13 @@ async fn page( let identifier = state.repo.identifier(hash.clone()).await?; let details = if let Some(identifier) = identifier { - state - .repo - .details(&identifier) - .await? - .map(|d| d.into_api_details()) + if let Some(details) = state.repo.details(&identifier).await? { + let blurhash = ensure_blurhash(&state, hash.clone(), Some(&details)).await?; + + Some(details.into_api_details(blurhash.to_string())) + } else { + None + } } else { None }; @@ -800,7 +823,7 @@ fn prepare_process( } #[tracing::instrument(name = "Fetching derived details", skip(state))] -async fn process_details( +async fn process_details( web::Query(ProcessQuery { source, operations }): web::Query, ext: web::Path, state: web::Data>, @@ -824,7 +847,7 @@ async fn process_details( let identifier = state .repo - .variant_identifier(hash, variant) + .variant_identifier(hash.clone(), variant) .await? .ok_or(UploadError::MissingAlias)?; @@ -832,7 +855,9 @@ async fn process_details( let details = details.ok_or(UploadError::NoFiles)?; - Ok(HttpResponse::Ok().json(details.into_api_details())) + let blurhash = ensure_blurhash(&state, hash, Some(&details)).await?; + + Ok(HttpResponse::Ok().json(details.into_api_details(blurhash.to_string()))) } async fn not_found_hash(repo: &ArcRepo) -> Result, Error> { @@ -1085,9 +1110,17 @@ async fn details_query( ) -> Result { let alias = alias_from_query(query, &state).await?; - let details = ensure_details(&state, &alias).await?; + let hash = state + .repo + .hash(&alias) + .await? + .ok_or(UploadError::MissingAlias)?; - Ok(HttpResponse::Ok().json(details.into_api_details())) + let details = ensure_details_hash(&state, hash.clone()).await?; + + let blurhash = ensure_blurhash(&state, hash, Some(&details)).await?; + + Ok(HttpResponse::Ok().json(details.into_api_details(blurhash.to_string()))) } /// Fetch file details @@ -1096,9 +1129,17 @@ async fn details( alias: web::Path>, state: web::Data>, ) -> Result { - let details = ensure_details(&state, &alias).await?; + let hash = state + .repo + .hash(&alias) + .await? + .ok_or(UploadError::MissingAlias)?; - Ok(HttpResponse::Ok().json(details.into_api_details())) + let details = ensure_details_hash(&state, hash.clone()).await?; + + let blurhash = ensure_blurhash(&state, hash, Some(&details)).await?; + + Ok(HttpResponse::Ok().json(details.into_api_details(blurhash.to_string()))) } /// Serve files based on alias query @@ -1393,16 +1434,7 @@ async fn blurhash( .await? .ok_or(UploadError::MissingAlias)?; - let blurhash = if let Some(blurhash) = state.repo.blurhash(hash.clone()).await? { - blurhash - } else { - let details = ensure_details_hash(&state, hash.clone()).await?; - let blurhash = blurhash::generate(&state, hash.clone(), &details).await?; - let blurhash: Arc = Arc::from(blurhash); - state.repo.relate_blurhash(hash, blurhash.clone()).await?; - - blurhash - }; + let blurhash = ensure_blurhash(&state, hash, None).await?; Ok(HttpResponse::Ok().json(serde_json::json!({ "msg": "ok", @@ -1410,6 +1442,28 @@ async fn blurhash( }))) } +async fn ensure_blurhash( + state: &State, + hash: Hash, + details: Option<&Details>, +) -> Result, Error> { + if let Some(blurhash) = state.repo.blurhash(hash.clone()).await? { + Ok(blurhash) + } else { + let blurhash = if let Some(details) = details { + blurhash::generate(state, hash.clone(), details).await? + } else { + let details = ensure_details_hash(state, hash.clone()).await?; + blurhash::generate(state, hash.clone(), &details).await? + }; + + let blurhash: Arc = Arc::from(blurhash); + state.repo.relate_blurhash(hash, blurhash.clone()).await?; + + Ok(blurhash) + } +} + #[derive(serde::Serialize)] struct PruneResponse { complete: bool,