count pages per version
This commit is contained in:
@@ -87,6 +87,7 @@ pub struct DocumentVersion {
|
||||
pub size_bytes: i64,
|
||||
pub checksum: String,
|
||||
pub created_at: NaiveDateTime,
|
||||
pub metadata: serde_json::Value,
|
||||
pub operations_summary: serde_json::Value,
|
||||
}
|
||||
|
||||
@@ -99,6 +100,7 @@ pub struct NewDocumentVersion {
|
||||
pub s3_key: String,
|
||||
pub size_bytes: i64,
|
||||
pub checksum: String,
|
||||
pub metadata: serde_json::Value,
|
||||
pub operations_summary: serde_json::Value,
|
||||
}
|
||||
|
||||
|
||||
@@ -107,6 +107,7 @@ pub struct DocumentVersionResponse {
|
||||
pub size_bytes: i64,
|
||||
pub checksum: String,
|
||||
pub created_at: String,
|
||||
pub metadata: Value,
|
||||
pub operations_summary: Value,
|
||||
}
|
||||
|
||||
@@ -1641,6 +1642,7 @@ async fn process_upload(
|
||||
s3_key: s3_key.clone(),
|
||||
size_bytes,
|
||||
checksum: checksum_hex.clone(),
|
||||
metadata: Value::Object(Default::default()),
|
||||
operations_summary: Value::Object(Default::default()),
|
||||
};
|
||||
|
||||
@@ -1871,6 +1873,7 @@ fn to_version_response(version: DocumentVersion) -> DocumentVersionResponse {
|
||||
size_bytes: version.size_bytes,
|
||||
checksum: version.checksum,
|
||||
created_at: to_iso(version.created_at),
|
||||
metadata: version.metadata,
|
||||
operations_summary: version.operations_summary,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ diesel::table! {
|
||||
checksum -> Varchar,
|
||||
created_at -> Timestamptz,
|
||||
operations_summary -> Jsonb,
|
||||
metadata -> Jsonb,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{io::Cursor, panic, sync::Arc, time::Duration};
|
||||
use std::{convert::TryInto, io::Cursor, panic, sync::Arc, time::Duration};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use chrono::Utc;
|
||||
@@ -6,7 +6,7 @@ use diesel::{pg::upsert::excluded, prelude::*};
|
||||
use image::{GenericImageView, ImageFormat, ImageReader};
|
||||
use pdfium_render::prelude::*;
|
||||
use serde::Deserialize;
|
||||
use serde_json::json;
|
||||
use serde_json::{json, Map, Value};
|
||||
use tokio::task;
|
||||
use tracing::{error, info, warn};
|
||||
use uuid::Uuid;
|
||||
@@ -103,6 +103,45 @@ impl JobHandler for GenerateThumbnailsJob {
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(page_count) = generation.page_count {
|
||||
let state_clone = state.clone();
|
||||
let document_id = initial.document.id;
|
||||
let version_id = initial.version.id;
|
||||
match task::spawn_blocking(move || {
|
||||
persist_document_page_count(state_clone, document_id, version_id, page_count)
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(Ok(())) => {}
|
||||
Ok(Err(err)) => {
|
||||
warn!(
|
||||
job_id = %job.id,
|
||||
document_id = %document_id,
|
||||
version_id = %version_id,
|
||||
error = %err,
|
||||
"failed to update document page count metadata; retrying"
|
||||
);
|
||||
return JobExecution::Retry {
|
||||
delay: Duration::from_secs(30),
|
||||
error: err,
|
||||
};
|
||||
}
|
||||
Err(join_err) => {
|
||||
error!(
|
||||
job_id = %job.id,
|
||||
document_id = %document_id,
|
||||
version_id = %version_id,
|
||||
error = %join_err,
|
||||
"page count metadata task panicked"
|
||||
);
|
||||
return JobExecution::Retry {
|
||||
delay: Duration::from_secs(60),
|
||||
error: format!("metadata panic: {join_err}"),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let thumbnail_asset_id = initial
|
||||
.existing_thumbnail
|
||||
.as_ref()
|
||||
@@ -224,6 +263,7 @@ struct GeneratedImage {
|
||||
struct GeneratedAssets {
|
||||
thumbnail: GeneratedImage,
|
||||
preview: GeneratedImage,
|
||||
page_count: Option<u32>,
|
||||
}
|
||||
|
||||
struct AssetPersistence<'a> {
|
||||
@@ -305,13 +345,21 @@ fn generate_preview_and_thumbnail(
|
||||
.unwrap_or(false)
|
||||
});
|
||||
|
||||
let (preview, thumbnail) = if is_pdf {
|
||||
generate_pdf_assets(bytes)?
|
||||
if is_pdf {
|
||||
let pdf_assets = generate_pdf_assets(bytes)?;
|
||||
Ok(GeneratedAssets {
|
||||
preview: pdf_assets.preview,
|
||||
thumbnail: pdf_assets.thumbnail,
|
||||
page_count: Some(pdf_assets.page_count),
|
||||
})
|
||||
} else {
|
||||
generate_image_assets(bytes)?
|
||||
};
|
||||
|
||||
Ok(GeneratedAssets { preview, thumbnail })
|
||||
let (preview, thumbnail) = generate_image_assets(bytes)?;
|
||||
Ok(GeneratedAssets {
|
||||
preview,
|
||||
thumbnail,
|
||||
page_count: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_image_assets(bytes: &[u8]) -> Result<(GeneratedImage, GeneratedImage), String> {
|
||||
@@ -339,7 +387,13 @@ fn generate_image_assets(bytes: &[u8]) -> Result<(GeneratedImage, GeneratedImage
|
||||
Ok((preview, thumbnail))
|
||||
}
|
||||
|
||||
fn generate_pdf_assets(bytes: &[u8]) -> Result<(GeneratedImage, GeneratedImage), String> {
|
||||
struct PdfGeneratedAssets {
|
||||
preview: GeneratedImage,
|
||||
thumbnail: GeneratedImage,
|
||||
page_count: u32,
|
||||
}
|
||||
|
||||
fn generate_pdf_assets(bytes: &[u8]) -> Result<PdfGeneratedAssets, String> {
|
||||
let pdfium = panic::catch_unwind(|| Pdfium::default())
|
||||
.map_err(|_| "failed to initialize PDFium".to_string())?;
|
||||
|
||||
@@ -347,8 +401,10 @@ fn generate_pdf_assets(bytes: &[u8]) -> Result<(GeneratedImage, GeneratedImage),
|
||||
.load_pdf_from_byte_slice(bytes, None)
|
||||
.map_err(|err| format!("load pdf: {err}"))?;
|
||||
|
||||
let page = document
|
||||
.pages()
|
||||
let pages = document.pages();
|
||||
let total_pages = pages.len();
|
||||
|
||||
let page = pages
|
||||
.get(0)
|
||||
.map_err(|err| format!("load first page: {err}"))?;
|
||||
|
||||
@@ -375,7 +431,15 @@ fn generate_pdf_assets(bytes: &[u8]) -> Result<(GeneratedImage, GeneratedImage),
|
||||
let preview = encode_dynamic_image(preview_image)?;
|
||||
let thumbnail = encode_dynamic_image(thumbnail_image)?;
|
||||
|
||||
Ok((preview, thumbnail))
|
||||
let page_count: u32 = total_pages
|
||||
.try_into()
|
||||
.map_err(|_| "page count exceeds supported range".to_string())?;
|
||||
|
||||
Ok(PdfGeneratedAssets {
|
||||
preview,
|
||||
thumbnail,
|
||||
page_count,
|
||||
})
|
||||
}
|
||||
|
||||
fn encode_dynamic_image(image: image::DynamicImage) -> Result<GeneratedImage, String> {
|
||||
@@ -430,3 +494,42 @@ fn persist_assets_metadata(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn persist_document_page_count(
|
||||
state: Arc<AppState>,
|
||||
document_id: Uuid,
|
||||
document_version_id: Uuid,
|
||||
page_count: u32,
|
||||
) -> Result<(), String> {
|
||||
let mut conn = state.db().map_err(|err| format!("{err:?}"))?;
|
||||
|
||||
let existing_metadata: Value = document_versions::table
|
||||
.filter(document_versions::id.eq(document_version_id))
|
||||
.filter(document_versions::document_id.eq(document_id))
|
||||
.select(document_versions::metadata)
|
||||
.first(&mut conn)
|
||||
.map_err(|err| format!("{err:?}"))?;
|
||||
|
||||
let updated = match existing_metadata {
|
||||
Value::Object(mut map) => {
|
||||
map.insert("page_count".to_string(), Value::from(page_count));
|
||||
Value::Object(map)
|
||||
}
|
||||
_ => {
|
||||
let mut map = Map::new();
|
||||
map.insert("page_count".to_string(), Value::from(page_count));
|
||||
Value::Object(map)
|
||||
}
|
||||
};
|
||||
|
||||
diesel::update(
|
||||
document_versions::table
|
||||
.filter(document_versions::id.eq(document_version_id))
|
||||
.filter(document_versions::document_id.eq(document_id)),
|
||||
)
|
||||
.set(document_versions::metadata.eq(updated))
|
||||
.execute(&mut conn)
|
||||
.map_err(|err| format!("{err:?}"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user