feat: expand media discovery and AI workflows #8

Merged
LyAhn merged 25 commits from codex/5-discovery-features into main 2026-06-07 22:43:16 +00:00
3 changed files with 30 additions and 7 deletions
Showing only changes of commit 20c43d2056 - Show all commits
+14
View File
@@ -263,6 +263,13 @@ pub fn migrate(conn: &Connection) -> Result<()> {
groups_json TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS app_kv (
key TEXT PRIMARY KEY,
value INTEGER NOT NULL DEFAULT 0
);
INSERT OR IGNORE INTO app_kv (key, value) VALUES ('embedding_revision', 0);
CREATE INDEX IF NOT EXISTS idx_images_folder_id ON images(folder_id);
CREATE INDEX IF NOT EXISTS idx_images_modified_at ON images(modified_at);
CREATE INDEX IF NOT EXISTS idx_embedding_jobs_status ON embedding_jobs(status);
@@ -915,6 +922,13 @@ pub fn mark_embedding_ready(conn: &Connection, image_id: i64, model: &str) -> Re
params![image_id, model],
)?;
conn.execute("DELETE FROM embedding_jobs WHERE image_id = ?1", [image_id])?;
// Advance the monotonic revision so the HNSW cache is always rebuilt after
// any embedding change, regardless of clock resolution.
conn.execute(
"INSERT INTO app_kv (key, value) VALUES ('embedding_revision', 1)
ON CONFLICT(key) DO UPDATE SET value = value + 1",
[],
)?;
Ok(())
}
+5
View File
@@ -373,6 +373,11 @@ fn do_index(app: AppHandle, pool: &DbPool, folder_id: i64, folder_path: PathBuf)
let _ = db::backfill_embedding_jobs(&conn)?;
db::update_folder_count(&conn, folder_id)?;
let _ = db::clear_folder_scan_error(&conn, folder_id);
// Invalidate duplicate scan cache — any reindex can change file contents
// or the set of files, making cached duplicate groups stale.
let folder_scope = format!("folder:{}", folder_id);
let _ = db::clear_duplicate_scan_cache(&conn, &folder_scope);
let _ = db::clear_duplicate_scan_cache(&conn, "all");
}
emit_progress(
+11 -7
View File
@@ -215,13 +215,17 @@ pub fn get_image_embedding(conn: &Connection, image_id: i64) -> Result<Option<Ve
}
pub fn get_embedding_revision(conn: &Connection) -> Result<String> {
let count: i64 = conn.query_row("SELECT COUNT(*) FROM image_vec", [], |row| row.get(0))?;
let max_updated_at: Option<String> = conn.query_row(
"SELECT MAX(embedding_updated_at) FROM images WHERE embedding_status = 'ready'",
[],
|row| row.get(0),
)?;
Ok(format!("{}:{}", count, max_updated_at.unwrap_or_default()))
// Use the monotonically incremented app_kv counter so that two embeddings
// saved within the same clock second still advance the revision, preventing
// the HNSW cache from serving stale vectors.
let revision: i64 = conn
.query_row(
"SELECT COALESCE((SELECT value FROM app_kv WHERE key = 'embedding_revision'), 0)",
[],
|row| row.get(0),
)
.unwrap_or(0);
Ok(revision.to_string())
}
// fn image_ids_for_folder(