fix: address 3 P2 review issues from PR #8 round 5
captioner: set_caption_detail now marks CAPTION_SESSION_DIRTY so the cached FlorenceCaptioner session is torn down and rebuilt with the new prompt and token limit; previously only the file was updated on disk db: reset_generated_captions now cancels in-flight caption jobs instead of deleting them; the worker checks for a cancelled row before writing back, so deleting the row allowed results to be written immediately after a reset — now mirrors the clear_caption_jobs cancellation protocol BackgroundTasks: caption_pending/ready/failed counts are now included in pendingMediaWork, task stages, hasFailed checks, and the dismiss snapshot; previously a folder with only caption work caused the panel to disappear entirely and exposed no caption progress or failure status
This commit is contained in:
@@ -238,6 +238,7 @@ pub fn set_caption_detail(app_data_dir: &Path, detail: CaptionDetail) -> Result<
|
|||||||
std::fs::create_dir_all(parent)?;
|
std::fs::create_dir_all(parent)?;
|
||||||
}
|
}
|
||||||
std::fs::write(path, detail.as_str())?;
|
std::fs::write(path, detail.as_str())?;
|
||||||
|
CAPTION_SESSION_DIRTY.store(true, Ordering::Relaxed);
|
||||||
Ok(detail)
|
Ok(detail)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+22
-2
@@ -693,9 +693,19 @@ pub fn reset_generated_captions(conn: &Connection, folder_id: Option<i64>) -> Re
|
|||||||
WHERE folder_id = ?1",
|
WHERE folder_id = ?1",
|
||||||
[folder_id],
|
[folder_id],
|
||||||
)?;
|
)?;
|
||||||
|
// Mark in-flight jobs cancelled so the worker skips writing back;
|
||||||
|
// delete all others.
|
||||||
|
tx.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],
|
||||||
|
)?;
|
||||||
tx.execute(
|
tx.execute(
|
||||||
"DELETE FROM caption_jobs
|
"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],
|
[folder_id],
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
@@ -708,7 +718,17 @@ pub fn reset_generated_captions(conn: &Connection, folder_id: Option<i64>) -> Re
|
|||||||
caption_error = NULL",
|
caption_error = NULL",
|
||||||
[],
|
[],
|
||||||
)?;
|
)?;
|
||||||
tx.execute("DELETE FROM caption_jobs", [])?;
|
// Same cancellation protocol as clear_caption_jobs.
|
||||||
|
tx.execute(
|
||||||
|
"UPDATE caption_jobs
|
||||||
|
SET status = 'cancelled', last_error = NULL, updated_at = datetime('now')
|
||||||
|
WHERE status = 'processing'",
|
||||||
|
[],
|
||||||
|
)?;
|
||||||
|
tx.execute(
|
||||||
|
"DELETE FROM caption_jobs WHERE status NOT IN ('processing', 'cancelled')",
|
||||||
|
[],
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ interface Task {
|
|||||||
stages: TaskStage[];
|
stages: TaskStage[];
|
||||||
hasFailedEmbeddings: boolean;
|
hasFailedEmbeddings: boolean;
|
||||||
hasFailedTagging: boolean;
|
hasFailedTagging: boolean;
|
||||||
|
hasFailedCaptions: boolean;
|
||||||
pendingMediaWork: number;
|
pendingMediaWork: number;
|
||||||
embeddingProcessed: number;
|
embeddingProcessed: number;
|
||||||
embeddingTotal: number;
|
embeddingTotal: number;
|
||||||
@@ -148,16 +149,22 @@ export function BackgroundTasks() {
|
|||||||
const taggingPending = jobs?.tagging_pending ?? 0;
|
const taggingPending = jobs?.tagging_pending ?? 0;
|
||||||
const taggingReady = jobs?.tagging_ready ?? 0;
|
const taggingReady = jobs?.tagging_ready ?? 0;
|
||||||
const taggingFailed = jobs?.tagging_failed ?? 0;
|
const taggingFailed = jobs?.tagging_failed ?? 0;
|
||||||
|
const captionPending = jobs?.caption_pending ?? 0;
|
||||||
|
const captionReady = jobs?.caption_ready ?? 0;
|
||||||
|
const captionFailed = jobs?.caption_failed ?? 0;
|
||||||
|
|
||||||
const pendingMediaWork = thumbnailPending + metadataPending + embeddingPending + taggingPending;
|
const pendingMediaWork = thumbnailPending + metadataPending + embeddingPending + taggingPending + captionPending;
|
||||||
const embeddingProcessed = embeddingReady + embeddingFailed;
|
const embeddingProcessed = embeddingReady + embeddingFailed;
|
||||||
const embeddingTotal = embeddingProcessed + embeddingPending;
|
const embeddingTotal = embeddingProcessed + embeddingPending;
|
||||||
const taggingProcessed = taggingReady + taggingFailed;
|
const taggingProcessed = taggingReady + taggingFailed;
|
||||||
const taggingTotal = taggingProcessed + taggingPending;
|
const taggingTotal = taggingProcessed + taggingPending;
|
||||||
|
const captionProcessed = captionReady + captionFailed;
|
||||||
|
const captionTotal = captionProcessed + captionPending;
|
||||||
const hasFailedEmbeddings = embeddingFailed > 0;
|
const hasFailedEmbeddings = embeddingFailed > 0;
|
||||||
const hasFailedTagging = taggingFailed > 0;
|
const hasFailedTagging = taggingFailed > 0;
|
||||||
|
const hasFailedCaptions = captionFailed > 0;
|
||||||
|
|
||||||
if (!index && pendingMediaWork === 0 && !hasFailedEmbeddings && !hasFailedTagging) return null;
|
if (!index && pendingMediaWork === 0 && !hasFailedEmbeddings && !hasFailedTagging && !hasFailedCaptions) return null;
|
||||||
|
|
||||||
const stages: TaskStage[] = [];
|
const stages: TaskStage[] = [];
|
||||||
|
|
||||||
@@ -209,6 +216,16 @@ export function BackgroundTasks() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (captionPending > 0) {
|
||||||
|
const pct = captionTotal > 0 ? (captionProcessed / captionTotal) * 100 : 0;
|
||||||
|
stages.push({
|
||||||
|
label: "Captions",
|
||||||
|
detail: `${captionProcessed.toLocaleString()} / ${captionTotal.toLocaleString()}`,
|
||||||
|
progress: pct,
|
||||||
|
failed: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (hasFailedEmbeddings && pendingMediaWork === 0) {
|
if (hasFailedEmbeddings && pendingMediaWork === 0) {
|
||||||
stages.push({
|
stages.push({
|
||||||
label: "Failed",
|
label: "Failed",
|
||||||
@@ -227,7 +244,16 @@ export function BackgroundTasks() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const snapshot = `${pendingMediaWork}:${embeddingFailed}:${taggingFailed}`;
|
if (hasFailedCaptions && pendingMediaWork === 0) {
|
||||||
|
stages.push({
|
||||||
|
label: "Failed",
|
||||||
|
detail: `${captionFailed.toLocaleString()} captions`,
|
||||||
|
progress: null,
|
||||||
|
failed: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const snapshot = `${pendingMediaWork}:${embeddingFailed}:${taggingFailed}:${captionFailed}`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: folder.id,
|
id: folder.id,
|
||||||
@@ -235,6 +261,7 @@ export function BackgroundTasks() {
|
|||||||
stages,
|
stages,
|
||||||
hasFailedEmbeddings,
|
hasFailedEmbeddings,
|
||||||
hasFailedTagging,
|
hasFailedTagging,
|
||||||
|
hasFailedCaptions,
|
||||||
pendingMediaWork,
|
pendingMediaWork,
|
||||||
embeddingProcessed,
|
embeddingProcessed,
|
||||||
embeddingTotal,
|
embeddingTotal,
|
||||||
@@ -262,6 +289,7 @@ export function BackgroundTasks() {
|
|||||||
}],
|
}],
|
||||||
hasFailedEmbeddings: false,
|
hasFailedEmbeddings: false,
|
||||||
hasFailedTagging: false,
|
hasFailedTagging: false,
|
||||||
|
hasFailedCaptions: false,
|
||||||
pendingMediaWork: 1,
|
pendingMediaWork: 1,
|
||||||
embeddingProcessed: 0,
|
embeddingProcessed: 0,
|
||||||
embeddingTotal: 0,
|
embeddingTotal: 0,
|
||||||
@@ -275,7 +303,7 @@ export function BackgroundTasks() {
|
|||||||
|
|
||||||
const primary = allTasks[0];
|
const primary = allTasks[0];
|
||||||
const extraCount = allTasks.length - 1;
|
const extraCount = allTasks.length - 1;
|
||||||
const hasFailed = tasks.some((t) => (t.hasFailedEmbeddings || t.hasFailedTagging) && t.pendingMediaWork === 0);
|
const hasFailed = tasks.some((t) => (t.hasFailedEmbeddings || t.hasFailedTagging || t.hasFailedCaptions) && t.pendingMediaWork === 0);
|
||||||
|
|
||||||
// Best progress bar value: use embedding progress if available (most informative),
|
// Best progress bar value: use embedding progress if available (most informative),
|
||||||
// otherwise tagging progress, otherwise fall back to scanning progress, otherwise indeterminate.
|
// otherwise tagging progress, otherwise fall back to scanning progress, otherwise indeterminate.
|
||||||
@@ -407,7 +435,7 @@ export function BackgroundTasks() {
|
|||||||
const taskTaggingStage = task.stages.find((s) => s.label === "Tags");
|
const taskTaggingStage = task.stages.find((s) => s.label === "Tags");
|
||||||
const taskScanningStage = task.stages.find((s) => s.label === "Scanning");
|
const taskScanningStage = task.stages.find((s) => s.label === "Scanning");
|
||||||
const taskBarProgress = taskEmbeddingStage?.progress ?? taskTaggingStage?.progress ?? taskScanningStage?.progress ?? null;
|
const taskBarProgress = taskEmbeddingStage?.progress ?? taskTaggingStage?.progress ?? taskScanningStage?.progress ?? null;
|
||||||
const taskHasFailed = (task.hasFailedEmbeddings || task.hasFailedTagging) && task.pendingMediaWork === 0;
|
const taskHasFailed = (task.hasFailedEmbeddings || task.hasFailedTagging || task.hasFailedCaptions) && task.pendingMediaWork === 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={task.id}>
|
<div key={task.id}>
|
||||||
|
|||||||
Reference in New Issue
Block a user