feat: multi-library foundation (schema + libraries module)
Adds a `libraries` registry table and threads library_id through per-instance metadata tables (image_exif, photo_insights, entity_photo_links, video_preview_clips). File-path columns renamed to rel_path to make the relative-to-root semantics explicit. Adds content_hash + size_bytes on image_exif to support future hash-keyed thumbnail/HLS dedup. Tags and favorites stay library-agnostic so they share across libraries by rel_path. Behavior is unchanged: a single primary library (id=1) is seeded from BASE_PATH on first boot; all handlers and DAOs route through it as a transitional shim until the API gains a library query param. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -86,10 +86,14 @@ impl InsightDao for SqliteInsightDao {
|
||||
let mut connection = self.connection.lock().expect("Unable to get InsightDao");
|
||||
|
||||
// Mark all existing insights for this file as no longer current
|
||||
diesel::update(photo_insights.filter(file_path.eq(&insight.file_path)))
|
||||
.set(is_current.eq(false))
|
||||
.execute(connection.deref_mut())
|
||||
.map_err(|_| anyhow::anyhow!("Update is_current error"))?;
|
||||
diesel::update(
|
||||
photo_insights
|
||||
.filter(library_id.eq(insight.library_id))
|
||||
.filter(rel_path.eq(&insight.file_path)),
|
||||
)
|
||||
.set(is_current.eq(false))
|
||||
.execute(connection.deref_mut())
|
||||
.map_err(|_| anyhow::anyhow!("Update is_current error"))?;
|
||||
|
||||
// Insert the new insight as current
|
||||
diesel::insert_into(photo_insights)
|
||||
@@ -99,7 +103,8 @@ impl InsightDao for SqliteInsightDao {
|
||||
|
||||
// Retrieve the inserted record (is_current = true)
|
||||
photo_insights
|
||||
.filter(file_path.eq(&insight.file_path))
|
||||
.filter(library_id.eq(insight.library_id))
|
||||
.filter(rel_path.eq(&insight.file_path))
|
||||
.filter(is_current.eq(true))
|
||||
.first::<PhotoInsight>(connection.deref_mut())
|
||||
.map_err(|_| anyhow::anyhow!("Query error"))
|
||||
@@ -118,7 +123,7 @@ impl InsightDao for SqliteInsightDao {
|
||||
let mut connection = self.connection.lock().expect("Unable to get InsightDao");
|
||||
|
||||
photo_insights
|
||||
.filter(file_path.eq(path))
|
||||
.filter(rel_path.eq(path))
|
||||
.filter(is_current.eq(true))
|
||||
.first::<PhotoInsight>(connection.deref_mut())
|
||||
.optional()
|
||||
@@ -138,7 +143,7 @@ impl InsightDao for SqliteInsightDao {
|
||||
let mut connection = self.connection.lock().expect("Unable to get InsightDao");
|
||||
|
||||
photo_insights
|
||||
.filter(file_path.eq(path))
|
||||
.filter(rel_path.eq(path))
|
||||
.order(generated_at.desc())
|
||||
.load::<PhotoInsight>(connection.deref_mut())
|
||||
.map_err(|_| anyhow::anyhow!("Query error"))
|
||||
@@ -156,7 +161,7 @@ impl InsightDao for SqliteInsightDao {
|
||||
|
||||
let mut connection = self.connection.lock().expect("Unable to get InsightDao");
|
||||
|
||||
diesel::delete(photo_insights.filter(file_path.eq(path)))
|
||||
diesel::delete(photo_insights.filter(rel_path.eq(path)))
|
||||
.execute(connection.deref_mut())
|
||||
.map(|_| ())
|
||||
.map_err(|_| anyhow::anyhow!("Delete error"))
|
||||
@@ -195,7 +200,7 @@ impl InsightDao for SqliteInsightDao {
|
||||
|
||||
diesel::update(
|
||||
photo_insights
|
||||
.filter(file_path.eq(path))
|
||||
.filter(rel_path.eq(path))
|
||||
.filter(is_current.eq(true)),
|
||||
)
|
||||
.set(approved.eq(Some(is_approved)))
|
||||
|
||||
Reference in New Issue
Block a user