mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-05-24 16:32:45 -04:00
feat: implement thumbnail generation for linked content identities and improve logging in responder
This commit is contained in:
@@ -342,7 +342,7 @@ async fn handle_create(
|
||||
debug!("✓ Generated content hash: {}", content_hash);
|
||||
|
||||
// Link the content identity
|
||||
if let Err(e) = EntryProcessor::link_to_content_identity(
|
||||
match EntryProcessor::link_to_content_identity(
|
||||
ctx,
|
||||
entry_id,
|
||||
path,
|
||||
@@ -351,9 +351,59 @@ async fn handle_create(
|
||||
)
|
||||
.await
|
||||
{
|
||||
warn!("Failed to link content identity for {}: {}", path.display(), e);
|
||||
} else {
|
||||
debug!("✓ Linked content identity for entry {}", entry_id);
|
||||
Ok(link_result) => {
|
||||
debug!("✓ Linked content identity for entry {}", entry_id);
|
||||
|
||||
// Generate thumbnails for the file
|
||||
if let Some(library) = context.get_library(library_id).await {
|
||||
debug!("→ Generating thumbnails for: {}", path.display());
|
||||
|
||||
// Get MIME type from content identity
|
||||
let mime_type = if let Ok(Some(ci)) = crate::infra::db::entities::content_identity::Entity::find_by_id(link_result.content_identity.id)
|
||||
.one(ctx.library_db())
|
||||
.await
|
||||
{
|
||||
if let Some(mime_id) = ci.mime_type_id {
|
||||
if let Ok(Some(mime_record)) = crate::infra::db::entities::mime_type::Entity::find_by_id(mime_id)
|
||||
.one(ctx.library_db())
|
||||
.await
|
||||
{
|
||||
Some(mime_record.mime_type)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(mime) = mime_type {
|
||||
match crate::ops::media::thumbnail::generate_thumbnails_for_file(
|
||||
&library,
|
||||
&link_result.content_identity.uuid.expect("ContentIdentity should have UUID"),
|
||||
path,
|
||||
&mime,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(count) if count > 0 => {
|
||||
debug!("✓ Generated {} thumbnails for: {}", count, path.display());
|
||||
}
|
||||
Ok(_) => {
|
||||
debug!("No thumbnails generated for: {}", path.display());
|
||||
}
|
||||
Err(e) => {
|
||||
debug!("Thumbnail generation failed for {}: {}", path.display(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Failed to link content identity for {}: {}", path.display(), e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
debug!("✗ Failed to generate content hash for {}", path.display());
|
||||
|
||||
@@ -20,3 +20,132 @@ pub use generator::{ImageGenerator, ThumbnailGenerator, ThumbnailInfo, VideoGene
|
||||
pub use job::{ThumbnailJob, ThumbnailJobConfig};
|
||||
pub use state::{ThumbnailEntry, ThumbnailPhase, ThumbnailState, ThumbnailStats};
|
||||
pub use utils::ThumbnailUtils;
|
||||
|
||||
use crate::library::Library;
|
||||
use crate::ops::sidecar::types::SidecarKind;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Generate thumbnails for a single file (optimized for watcher/responder use)
|
||||
///
|
||||
/// This function generates the default thumbnail variants for a single file
|
||||
/// and registers them as sidecars. It's designed to be called inline for
|
||||
/// individual file creates/updates rather than dispatching a job.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `library` - The library context
|
||||
/// * `content_uuid` - The UUID of the content identity
|
||||
/// * `source_path` - The filesystem path to the source file
|
||||
/// * `mime_type` - The MIME type of the file
|
||||
///
|
||||
/// # Returns
|
||||
/// Number of thumbnails successfully generated
|
||||
pub async fn generate_thumbnails_for_file(
|
||||
library: &Arc<Library>,
|
||||
content_uuid: &Uuid,
|
||||
source_path: &Path,
|
||||
mime_type: &str,
|
||||
) -> ThumbnailResult<usize> {
|
||||
use tracing::{debug, warn};
|
||||
|
||||
// Check if thumbnail generation is supported for this file type
|
||||
if !ThumbnailUtils::is_thumbnail_supported(mime_type) {
|
||||
debug!("Thumbnail generation not supported for MIME type: {}", mime_type);
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
// Get sidecar manager
|
||||
let sidecar_manager = library
|
||||
.core_context()
|
||||
.get_sidecar_manager()
|
||||
.await
|
||||
.ok_or_else(|| ThumbnailError::other("SidecarManager not available"))?;
|
||||
|
||||
// Create thumbnail generator for this MIME type
|
||||
let generator = ThumbnailGenerator::for_mime_type(mime_type)?;
|
||||
|
||||
// Generate default variants (grid@1x, grid@2x, detail@1x)
|
||||
let variants = ThumbnailVariants::defaults();
|
||||
let mut generated_count = 0;
|
||||
|
||||
for variant_config in variants {
|
||||
// Check if thumbnail already exists
|
||||
if sidecar_manager
|
||||
.exists(
|
||||
&library.id(),
|
||||
content_uuid,
|
||||
&SidecarKind::Thumb,
|
||||
&variant_config.variant,
|
||||
&variant_config.format(),
|
||||
)
|
||||
.await
|
||||
.unwrap_or(false)
|
||||
{
|
||||
debug!(
|
||||
"Thumbnail already exists for {}: {}",
|
||||
content_uuid,
|
||||
variant_config.variant.as_str()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compute output path
|
||||
let output_path = sidecar_manager
|
||||
.compute_path(
|
||||
&library.id(),
|
||||
content_uuid,
|
||||
&SidecarKind::Thumb,
|
||||
&variant_config.variant,
|
||||
&variant_config.format(),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| ThumbnailError::other(format!("Failed to compute path: {}", e)))?;
|
||||
|
||||
// Generate thumbnail
|
||||
match generator
|
||||
.generate(source_path, &output_path.absolute_path, variant_config.size, variant_config.quality)
|
||||
.await
|
||||
{
|
||||
Ok(thumbnail_info) => {
|
||||
// Record the sidecar in the database
|
||||
if let Err(e) = sidecar_manager
|
||||
.record_sidecar(
|
||||
library,
|
||||
content_uuid,
|
||||
&SidecarKind::Thumb,
|
||||
&variant_config.variant,
|
||||
&variant_config.format(),
|
||||
thumbnail_info.size_bytes as u64,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
{
|
||||
warn!(
|
||||
"Failed to record sidecar for {}: {}",
|
||||
variant_config.variant.as_str(),
|
||||
e
|
||||
);
|
||||
} else {
|
||||
debug!(
|
||||
"✓ Generated thumbnail {}: {}x{}",
|
||||
variant_config.variant.as_str(),
|
||||
thumbnail_info.dimensions.0,
|
||||
thumbnail_info.dimensions.1
|
||||
);
|
||||
generated_count += 1;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
warn!(
|
||||
"Failed to generate thumbnail {} for {}: {}",
|
||||
variant_config.variant.as_str(),
|
||||
source_path.display(),
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(generated_count)
|
||||
}
|
||||
|
||||
@@ -224,18 +224,8 @@ export function useNormalizedCache<I, O>({
|
||||
// The filter checks parent path for Physical paths, which is correct for new files
|
||||
const shouldAppend = resourceFilter(resource);
|
||||
|
||||
console.log('[useNormalizedCache] Batch - checking resource:', {
|
||||
name: resource.name,
|
||||
id: resource.id,
|
||||
shouldAppend,
|
||||
seenIds: Array.from(seenIds)
|
||||
});
|
||||
|
||||
if (shouldAppend) {
|
||||
newData.push(resource);
|
||||
console.log('[useNormalizedCache] ✓ Appended new resource:', resource.name);
|
||||
} else {
|
||||
console.log('[useNormalizedCache] ✗ Rejected resource (filter returned false):', resource.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user