show noops in done column, add search bars, remove idle card, fix single-job drain, prevent noop vanish on toggle
Build and Push Docker Image / build (push) Has been cancelled

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-21 18:14:54 +02:00
parent d38e4d4290
commit 6b9606a05b
11 changed files with 104 additions and 49 deletions
+4 -4
View File
@@ -63,7 +63,7 @@ function emitQueueStatus(
for (const l of jobListeners) l(line);
}
async function runSequential(initial: Job[]): Promise<void> {
async function runSequential(initial: Job[], { drain = true } = {}): Promise<void> {
if (queueRunning) return;
queueRunning = true;
queueAbort = new AbortController();
@@ -115,8 +115,8 @@ async function runSequential(initial: Job[]): Promise<void> {
// When the local queue drains, re-check the DB for jobs that were
// approved mid-run. Without this they'd sit pending until the user
// manually clicks "Run all" again. Skip if aborted — user wants to stop.
if (queue.length === 0 && !signal.aborted) {
// manually clicks "Run all" again. Skip if aborted or drain=false.
if (drain && queue.length === 0 && !signal.aborted) {
const more = db.prepare("SELECT * FROM jobs WHERE status = 'pending' ORDER BY created_at").all() as Job[];
enqueueUnseenJobs(queue, seen, more);
}
@@ -253,7 +253,7 @@ app.post("/job/:id/run", async (c) => {
if (!result) return c.notFound();
return c.json(result);
}
runSequential([job]).catch((err) => logError(`Job ${job.id} failed:`, err));
runSequential([job], { drain: false }).catch((err) => logError(`Job ${job.id} failed:`, err));
const result = loadJobRow(jobId);
if (!result) return c.notFound();
return c.json(result);
+24 -11
View File
@@ -526,7 +526,14 @@ function recomputePlanAfterToggle(db: ReturnType<typeof getDb>, itemId: number):
}
const isNoop = !anyAudioRemoved && !audioOrderChanged && !hasSubs && !needsTranscode;
db.prepare("UPDATE review_plans SET is_noop = ? WHERE id = ?").run(isNoop ? 1 : 0, plan.id);
// Only flip is_noop to 1 when the plan is unsorted (inbox). If the user is
// actively reviewing a sorted plan, marking all tracks "keep" should NOT
// make the card vanish — the Review/Queue queries filter out noops.
const planRow = db.prepare("SELECT sorted FROM review_plans WHERE id = ?").get(plan.id) as { sorted: number };
if (!isNoop || !planRow.sorted) {
db.prepare("UPDATE review_plans SET is_noop = ? WHERE id = ?").run(isNoop ? 1 : 0, plan.id);
}
}
// ─── Pipeline: summary ───────────────────────────────────────────────────────
@@ -865,7 +872,7 @@ app.get("/pipeline", (c) => {
`)
.all();
const done = db
const doneJobs = db
.prepare(`
SELECT j.*, mi.name, mi.series_name, mi.type,
rp.job_type, rp.apple_compat
@@ -877,15 +884,21 @@ app.get("/pipeline", (c) => {
`)
.all();
// "Done" = files already in the desired end state. Either the analyzer
// says nothing to do (is_noop=1) or a job finished. Use two indexable
// counts and add — the OR form (is_noop=1 OR status='done') can't use
// our single-column indexes and gets slow on large libraries.
const noopRow = db.prepare("SELECT COUNT(*) as n FROM review_plans WHERE is_noop = 1").get() as { n: number };
const doneRow = db.prepare("SELECT COUNT(*) as n FROM review_plans WHERE status = 'done' AND is_noop = 0").get() as {
n: number;
};
const doneCount = noopRow.n + doneRow.n;
// Noop items (already in desired state, no job needed) also belong in Done.
const noopItems = db
.prepare(`
SELECT rp.item_id, mi.name, mi.series_name, mi.type,
rp.job_type, rp.apple_compat, rp.is_noop,
'noop' as status
FROM review_plans rp
JOIN media_items mi ON mi.id = rp.item_id
WHERE rp.is_noop = 1
ORDER BY mi.name
`)
.all();
const done = [...doneJobs, ...noopItems];
const doneCount = done.length;
enrichWithStreamsAndReasons(db, queued as EnrichableRow[]);