From d741e8d58505b5dbcb2305a3232959b9f6454a79 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Mon, 9 Feb 2026 15:38:11 +0300 Subject: [PATCH] various: add `links_extracted_at` field to track markdown link extraction time Signed-off-by: NotAShelf Change-Id: Id13c6243de4c0f4fa5a87a13402379906a6a6964 --- crates/pinakes-core/src/storage/postgres.rs | 25 ++++++++++----- crates/pinakes-core/src/storage/sqlite.rs | 35 +++++++++++++++++---- crates/pinakes-server/src/dto.rs | 6 ++++ crates/pinakes-ui/src/client.rs | 2 ++ crates/pinakes-ui/src/components/detail.rs | 9 ++++++ 5 files changed, 64 insertions(+), 13 deletions(-) diff --git a/crates/pinakes-core/src/storage/postgres.rs b/crates/pinakes-core/src/storage/postgres.rs index ed5633f..3ee49ab 100644 --- a/crates/pinakes-core/src/storage/postgres.rs +++ b/crates/pinakes-core/src/storage/postgres.rs @@ -701,7 +701,9 @@ impl StorageBackend for PostgresBackend { "SELECT id, path, file_name, media_type, content_hash, file_size, title, artist, album, genre, year, duration_secs, description, thumbnail_path, file_mtime, date_taken, latitude, longitude, - camera_make, camera_model, rating, perceptual_hash, created_at, updated_at + camera_make, camera_model, rating, perceptual_hash, + storage_mode, original_filename, uploaded_at, storage_key, + created_at, updated_at, deleted_at, links_extracted_at FROM media_items WHERE id = $1", &[&id.0], ) @@ -725,7 +727,9 @@ impl StorageBackend for PostgresBackend { "SELECT id, path, file_name, media_type, content_hash, file_size, title, artist, album, genre, year, duration_secs, description, thumbnail_path, file_mtime, date_taken, latitude, longitude, - camera_make, camera_model, rating, perceptual_hash, created_at, updated_at + camera_make, camera_model, rating, perceptual_hash, + storage_mode, original_filename, uploaded_at, storage_key, + created_at, updated_at, deleted_at, links_extracted_at FROM media_items WHERE content_hash = $1", &[&hash.0], ) @@ -754,7 +758,9 @@ impl StorageBackend for PostgresBackend { "SELECT id, path, file_name, media_type, content_hash, file_size, title, artist, album, genre, year, duration_secs, description, thumbnail_path, file_mtime, date_taken, latitude, longitude, - camera_make, camera_model, rating, perceptual_hash, created_at, updated_at + camera_make, camera_model, rating, perceptual_hash, + storage_mode, original_filename, uploaded_at, storage_key, + created_at, updated_at, deleted_at, links_extracted_at FROM media_items WHERE path = $1", &[&path_str], ) @@ -794,7 +800,7 @@ impl StorageBackend for PostgresBackend { thumbnail_path, file_mtime, date_taken, latitude, longitude, camera_make, camera_model, rating, perceptual_hash, storage_mode, original_filename, uploaded_at, storage_key, - created_at, updated_at, deleted_at + created_at, updated_at, deleted_at, links_extracted_at FROM media_items WHERE deleted_at IS NULL ORDER BY {order_by} @@ -1345,7 +1351,10 @@ impl StorageBackend for PostgresBackend { .query( "SELECT m.id, m.path, m.file_name, m.media_type, m.content_hash, m.file_size, m.title, m.artist, m.album, m.genre, m.year, m.duration_secs, - m.description, m.thumbnail_path, m.created_at, m.updated_at + m.description, m.thumbnail_path, m.file_mtime, m.date_taken, m.latitude, + m.longitude, m.camera_make, m.camera_model, m.rating, m.perceptual_hash, + m.storage_mode, m.original_filename, m.uploaded_at, m.storage_key, + m.created_at, m.updated_at, m.deleted_at, m.links_extracted_at FROM media_items m JOIN collection_members cm ON cm.media_id = m.id WHERE cm.collection_id = $1 @@ -1449,7 +1458,8 @@ impl StorageBackend for PostgresBackend { m.title, m.artist, m.album, m.genre, m.year, m.duration_secs, m.description, m.thumbnail_path, m.file_mtime, m.date_taken, m.latitude, m.longitude, m.camera_make, m.camera_model, m.rating, m.perceptual_hash, - m.created_at, m.updated_at, + m.storage_mode, m.original_filename, m.uploaded_at, m.storage_key, + m.created_at, m.updated_at, m.deleted_at, m.links_extracted_at, ts_rank(m.search_vector, plainto_tsquery('english', ${fts_param_idx})) AS rank FROM media_items m WHERE {full_where} @@ -1466,7 +1476,8 @@ impl StorageBackend for PostgresBackend { m.title, m.artist, m.album, m.genre, m.year, m.duration_secs, m.description, m.thumbnail_path, m.file_mtime, m.date_taken, m.latitude, m.longitude, m.camera_make, m.camera_model, m.rating, m.perceptual_hash, - m.created_at, m.updated_at + m.storage_mode, m.original_filename, m.uploaded_at, m.storage_key, + m.created_at, m.updated_at, m.deleted_at, m.links_extracted_at FROM media_items m WHERE {full_where} ORDER BY {order_by} diff --git a/crates/pinakes-core/src/storage/sqlite.rs b/crates/pinakes-core/src/storage/sqlite.rs index bfd471b..74e28b1 100644 --- a/crates/pinakes-core/src/storage/sqlite.rs +++ b/crates/pinakes-core/src/storage/sqlite.rs @@ -727,7 +727,11 @@ impl StorageBackend for SqliteBackend { let mut stmt = db.prepare( "SELECT id, path, file_name, media_type, content_hash, file_size, \ title, artist, album, genre, year, duration_secs, description, \ - thumbnail_path, file_mtime, created_at, updated_at FROM media_items WHERE id = ?1", + thumbnail_path, file_mtime, date_taken, latitude, longitude, \ + camera_make, camera_model, rating, perceptual_hash, \ + storage_mode, original_filename, uploaded_at, storage_key, \ + created_at, updated_at, deleted_at, links_extracted_at \ + FROM media_items WHERE id = ?1", )?; let mut item = stmt .query_row(params![id.0.to_string()], row_to_media_item) @@ -754,7 +758,11 @@ impl StorageBackend for SqliteBackend { let mut stmt = db.prepare( "SELECT id, path, file_name, media_type, content_hash, file_size, \ title, artist, album, genre, year, duration_secs, description, \ - thumbnail_path, file_mtime, created_at, updated_at FROM media_items WHERE content_hash = ?1", + thumbnail_path, file_mtime, date_taken, latitude, longitude, \ + camera_make, camera_model, rating, perceptual_hash, \ + storage_mode, original_filename, uploaded_at, storage_key, \ + created_at, updated_at, deleted_at, links_extracted_at \ + FROM media_items WHERE content_hash = ?1", )?; let result = stmt .query_row(params![hash.0], row_to_media_item) @@ -780,7 +788,11 @@ impl StorageBackend for SqliteBackend { let mut stmt = db.prepare( "SELECT id, path, file_name, media_type, content_hash, file_size, \ title, artist, album, genre, year, duration_secs, description, \ - thumbnail_path, file_mtime, created_at, updated_at FROM media_items WHERE path = ?1", + thumbnail_path, file_mtime, date_taken, latitude, longitude, \ + camera_make, camera_model, rating, perceptual_hash, \ + storage_mode, original_filename, uploaded_at, storage_key, \ + created_at, updated_at, deleted_at, links_extracted_at \ + FROM media_items WHERE path = ?1", )?; let result = stmt .query_row(params![path_str], row_to_media_item) @@ -817,7 +829,11 @@ impl StorageBackend for SqliteBackend { let sql = format!( "SELECT id, path, file_name, media_type, content_hash, file_size, \ title, artist, album, genre, year, duration_secs, description, \ - thumbnail_path, file_mtime, created_at, updated_at FROM media_items \ + thumbnail_path, file_mtime, date_taken, latitude, longitude, \ + camera_make, camera_model, rating, perceptual_hash, \ + storage_mode, original_filename, uploaded_at, storage_key, \ + created_at, updated_at, deleted_at, links_extracted_at \ + FROM media_items \ WHERE deleted_at IS NULL \ ORDER BY {order_by} LIMIT ?1 OFFSET ?2" ); @@ -1239,7 +1255,10 @@ impl StorageBackend for SqliteBackend { let mut stmt = db.prepare( "SELECT m.id, m.path, m.file_name, m.media_type, m.content_hash, m.file_size, \ m.title, m.artist, m.album, m.genre, m.year, m.duration_secs, m.description, \ - m.thumbnail_path, m.created_at, m.updated_at \ + m.thumbnail_path, m.file_mtime, m.date_taken, m.latitude, m.longitude, \ + m.camera_make, m.camera_model, m.rating, m.perceptual_hash, \ + m.storage_mode, m.original_filename, m.uploaded_at, m.storage_key, \ + m.created_at, m.updated_at, m.deleted_at, m.links_extracted_at \ FROM media_items m \ JOIN collection_members cm ON cm.media_id = m.id \ WHERE cm.collection_id = ?1 \ @@ -1275,7 +1294,11 @@ impl StorageBackend for SqliteBackend { let mut sql = String::from( "SELECT m.id, m.path, m.file_name, m.media_type, m.content_hash, m.file_size, \ m.title, m.artist, m.album, m.genre, m.year, m.duration_secs, m.description, \ - m.thumbnail_path, m.created_at, m.updated_at FROM media_items m ", + m.thumbnail_path, m.file_mtime, m.date_taken, m.latitude, m.longitude, \ + m.camera_make, m.camera_model, m.rating, m.perceptual_hash, \ + m.storage_mode, m.original_filename, m.uploaded_at, m.storage_key, \ + m.created_at, m.updated_at, m.deleted_at, m.links_extracted_at \ + FROM media_items m ", ); if use_fts { diff --git a/crates/pinakes-server/src/dto.rs b/crates/pinakes-server/src/dto.rs index f7204fa..c35cbf5 100644 --- a/crates/pinakes-server/src/dto.rs +++ b/crates/pinakes-server/src/dto.rs @@ -34,6 +34,9 @@ pub struct MediaResponse { pub created_at: DateTime, pub updated_at: DateTime, + + // Markdown links + pub links_extracted_at: Option>, } #[derive(Debug, Serialize)] @@ -562,6 +565,9 @@ impl From for MediaResponse { created_at: item.created_at, updated_at: item.updated_at, + + // Markdown links + links_extracted_at: item.links_extracted_at, } } } diff --git a/crates/pinakes-ui/src/client.rs b/crates/pinakes-ui/src/client.rs index a52860b..7099d50 100644 --- a/crates/pinakes-ui/src/client.rs +++ b/crates/pinakes-ui/src/client.rs @@ -53,6 +53,8 @@ pub struct MediaResponse { pub custom_fields: HashMap, pub created_at: String, pub updated_at: String, + #[serde(default)] + pub links_extracted_at: Option, } #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] diff --git a/crates/pinakes-ui/src/components/detail.rs b/crates/pinakes-ui/src/components/detail.rs index b478f1a..3d38117 100644 --- a/crates/pinakes-ui/src/components/detail.rs +++ b/crates/pinakes-ui/src/components/detail.rs @@ -634,6 +634,15 @@ pub fn Detail( span { class: "detail-label", "Updated" } span { class: "detail-value", "{media.updated_at}" } } + // Links extracted timestamp (only for markdown files) + if media.media_type == "md" || media.media_type == "markdown" { + if let Some(links_time) = &media.links_extracted_at { + div { class: "detail-field", + span { class: "detail-label", "Links Extracted" } + span { class: "detail-value", "{links_time}" } + } + } + } } }