diff --git a/package.json b/package.json index 0679928..9a6d434 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "netfelix-audio-fix", - "version": "2026.04.21.6", + "version": "2026.04.21.7", "scripts": { "dev:server": "NODE_ENV=development bun --hot server/index.tsx", "dev:client": "vite", diff --git a/server/api/review.ts b/server/api/review.ts index eaf46f4..0208ea2 100644 --- a/server/api/review.ts +++ b/server/api/review.ts @@ -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 ──────────────────────────────────────────────────── diff --git a/server/api/scan.ts b/server/api/scan.ts index 010b094..d8999d3 100644 --- a/server/api/scan.ts +++ b/server/api/scan.ts @@ -235,14 +235,8 @@ async function runScan(limit: number | null = null): Promise { 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(); } } diff --git a/server/api/settings.ts b/server/api/settings.ts index d7e0e4b..f08ed24 100644 --- a/server/api/settings.ts +++ b/server/api/settings.ts @@ -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 }); });