fix stop buttons: centralize processInbox launch through abort-aware startProcessInbox
Build and Push Docker Image / build (push) Successful in 1m18s

All three processInbox callers (manual button, auto-processing toggle,
post-scan auto-process) now go through startProcessInbox() which manages
the shared abort controller. Previously only the manual button set the
abort controller, so Stop Sorting had no effect when processing was
triggered from the settings toggle or after scan completion.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-21 10:18:57 +02:00
parent 6721b8caf3
commit 78d569189f
4 changed files with 30 additions and 27 deletions
+25 -10
View File
@@ -1090,14 +1090,17 @@ app.post("/approve-batch", async (c) => {
// update progressively via SSE events.
let processInboxAbort: AbortController | null = null;
app.post("/process-inbox", async (c) => {
if (processInboxAbort) {
return c.json({ ok: false, error: "processing already running" }, 409);
}
/**
* Single entry point for launching processInbox. Manages the abort controller
* so every caller (manual button, auto-process toggle, post-scan auto-process)
* can be stopped via the /process-inbox/stop endpoint.
* Returns false if a run is already in progress.
*/
export function startProcessInbox(): boolean {
if (processInboxAbort) return false;
processInboxAbort = new AbortController();
const { signal } = processInboxAbort;
// Fire and forget — the frontend tracks progress via SSE events.
const db = getDb();
processInbox(db, getAudioLanguages(), undefined, {
onStart: emitInboxSortStart,
@@ -1110,15 +1113,27 @@ app.post("/process-inbox", async (c) => {
processInboxAbort = null;
});
return true;
}
export function stopProcessInbox(): boolean {
if (processInboxAbort) {
processInboxAbort.abort();
return true;
}
return false;
}
app.post("/process-inbox", async (c) => {
if (!startProcessInbox()) {
return c.json({ ok: false, error: "processing already running" }, 409);
}
return c.json({ ok: true });
});
app.post("/process-inbox/stop", (c) => {
if (processInboxAbort) {
processInboxAbort.abort();
return c.json({ ok: true });
}
return c.json({ ok: true, message: "not running" });
const stopped = stopProcessInbox();
return c.json({ ok: true, stopped });
});
// ─── Process single item ────────────────────────────────────────────────────
+2 -8
View File
@@ -235,14 +235,8 @@ async function runScan(limit: number | null = null): Promise<void> {
emitSse("complete", { scanned: processed, total, errors });
if (getConfig("auto_processing") === "1") {
const { processInbox, getAudioLanguages } = await import("./review");
const { emitInboxSorted, emitInboxSortStart, emitInboxSortProgress } = await import("./execute");
processInbox(db, getAudioLanguages(), undefined, {
onStart: emitInboxSortStart,
onProgress: emitInboxSortProgress,
})
.then((result) => emitInboxSorted(result))
.catch(() => emitInboxSorted({ moved_to_queue: 0, moved_to_review: 0 }));
const { startProcessInbox } = await import("./review");
startProcessInbox();
}
}
+2 -8
View File
@@ -90,14 +90,8 @@ app.post("/auto-processing", async (c) => {
setConfig("auto_processing", body.enabled ? "1" : "0");
if (body.enabled) {
const { processInbox, getAudioLanguages } = await import("./review");
const { emitInboxSorted, emitInboxSortStart, emitInboxSortProgress } = await import("./execute");
processInbox(getDb(), getAudioLanguages(), undefined, {
onStart: emitInboxSortStart,
onProgress: emitInboxSortProgress,
})
.then((result) => emitInboxSorted(result))
.catch(() => emitInboxSorted({ moved_to_queue: 0, moved_to_review: 0 }));
const { startProcessInbox } = await import("./review");
startProcessInbox();
}
return c.json({ ok: true, enabled: body.enabled });
});