fix: four more correctness bugs from PR review

- Duplicate scanner now emits one DuplicateGroup per distinct full hash
  within a sample-hash bucket, preventing disjoint sets (A,A vs B,B)
  from being merged and presented as a single deletable group
- Tagger model download now installs the shared ONNX runtime DLLs via
  ensure_onnx_runtime so the tagger is ready on a clean install without
  requiring the caption model to have been downloaded first
- Caption queue clear now follows the tagging worker pattern: marks
  in-flight rows as cancelled and deletes only non-processing rows;
  the caption worker skips writing results for cancelled jobs; startup
  cleanup removes any leftover cancelled caption rows
- Region search pagination now stores the crop rect in state and uses
  find_similar_by_region for subsequent pages instead of falling through
  to loadImages which appended unrelated folder results
This commit is contained in:
2026-06-06 22:00:20 +01:00
parent f82002129a
commit de0c2ab12d
5 changed files with 91 additions and 31 deletions
+32 -4
View File
@@ -511,6 +511,7 @@ pub fn reset_inflight_jobs(conn: &Connection) -> Result<()> {
// Delete any rows that were cancelled before the previous shutdown so
// they don't silently linger in the DB across restarts.
conn.execute("DELETE FROM tagging_jobs WHERE status = 'cancelled'", [])?;
conn.execute("DELETE FROM caption_jobs WHERE status = 'cancelled'", [])?;
Ok(())
}
@@ -588,13 +589,22 @@ pub fn requeue_processing_caption_jobs_for_folder(
}
pub fn clear_caption_jobs(conn: &Connection, folder_id: Option<i64>) -> Result<usize> {
// Mirror the tagging worker pattern: mark in-flight rows as cancelled so
// the worker can detect cancellation before writing results, then delete
// every non-processing row immediately (pending, failed, etc.).
match folder_id {
Some(folder_id) => {
conn.execute(
"UPDATE caption_jobs
SET status = 'cancelled', last_error = NULL, updated_at = datetime('now')
WHERE status = 'processing'
AND image_id IN (SELECT id FROM images WHERE folder_id = ?1)",
[folder_id],
)?;
let deleted = conn.execute(
"DELETE FROM caption_jobs
WHERE image_id IN (
SELECT id FROM images WHERE folder_id = ?1
)",
WHERE status NOT IN ('processing', 'cancelled')
AND image_id IN (SELECT id FROM images WHERE folder_id = ?1)",
[folder_id],
)?;
conn.execute(
@@ -607,7 +617,16 @@ pub fn clear_caption_jobs(conn: &Connection, folder_id: Option<i64>) -> Result<u
Ok(deleted)
}
None => {
let deleted = conn.execute("DELETE FROM caption_jobs", [])?;
conn.execute(
"UPDATE caption_jobs
SET status = 'cancelled', last_error = NULL, updated_at = datetime('now')
WHERE status = 'processing'",
[],
)?;
let deleted = conn.execute(
"DELETE FROM caption_jobs WHERE status NOT IN ('processing', 'cancelled')",
[],
)?;
conn.execute(
"UPDATE images
SET caption_error = NULL
@@ -619,6 +638,15 @@ pub fn clear_caption_jobs(conn: &Connection, folder_id: Option<i64>) -> Result<u
}
}
pub fn is_caption_job_cancelled(conn: &Connection, image_id: i64) -> Result<bool> {
let count: i64 = conn.query_row(
"SELECT COUNT(*) FROM caption_jobs WHERE image_id = ?1 AND status = 'cancelled'",
[image_id],
|row| row.get(0),
)?;
Ok(count > 0)
}
pub fn reset_generated_captions(conn: &Connection, folder_id: Option<i64>) -> Result<usize> {
let image_ids = match folder_id {
Some(folder_id) => {