done column: 'verify N' header button to backfill ✓ → ✓✓
All checks were successful
Build and Push Docker Image / build (push) Successful in 1m5s
All checks were successful
Build and Push Docker Image / build (push) Successful in 1m5s
new POST /api/execute/verify-unverified that picks every plan with status=done + verified=0 and runs handOffToJellyfin sequentially in the background. each handoff fires the existing plan_update sse so the done column promotes cards as jellyfin's verdict lands. exported handOffToJellyfin so the route can reuse the same flow as a fresh job. done column header shows a 'Verify N' action whenever there are unverified done plans, alongside the existing 'Clear'. one click and the user can backfill ✓✓ across every legacy done item without re-transcoding. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "netfelix-audio-fix",
|
||||
"version": "2026.04.14.23",
|
||||
"version": "2026.04.14.24",
|
||||
"scripts": {
|
||||
"dev:server": "NODE_ENV=development bun --hot server/index.tsx",
|
||||
"dev:client": "vite",
|
||||
|
||||
@@ -35,7 +35,7 @@ import type { Job, MediaItem, MediaStream } from "../types";
|
||||
* match the plan, so the check always passed immediately. Jellyfin is the
|
||||
* independent observer that matters.
|
||||
*/
|
||||
async function handOffToJellyfin(itemId: number): Promise<void> {
|
||||
export async function handOffToJellyfin(itemId: number): Promise<void> {
|
||||
const db = getDb();
|
||||
const row = db.prepare("SELECT jellyfin_id FROM media_items WHERE id = ?").get(itemId) as
|
||||
| { jellyfin_id: string }
|
||||
@@ -354,6 +354,40 @@ app.post("/clear-completed", (c) => {
|
||||
return c.json({ ok: true, cleared: result.changes });
|
||||
});
|
||||
|
||||
// ─── Verify all unverified done plans ────────────────────────────────────────
|
||||
// Backfill: kicks off the post-job jellyfin handoff for every plan that's
|
||||
// status=done + verified=0. Sequential with a small inter-call delay to
|
||||
// avoid hammering jellyfin's metadata refresher (each one waits up to 15s
|
||||
// for DateLastRefreshed to advance). Returns immediately with the count;
|
||||
// each individual handoff emits a plan_update SSE so the UI promotes ✓ → ✓✓
|
||||
// (or flips back to Review on disagreement) as it lands.
|
||||
app.post("/verify-unverified", (c) => {
|
||||
const db = getDb();
|
||||
const rows = db
|
||||
.prepare(`
|
||||
SELECT mi.id as item_id FROM review_plans rp
|
||||
JOIN media_items mi ON mi.id = rp.item_id
|
||||
WHERE rp.status = 'done' AND rp.verified = 0
|
||||
ORDER BY rp.reviewed_at DESC NULLS LAST
|
||||
`)
|
||||
.all() as { item_id: number }[];
|
||||
|
||||
if (rows.length === 0) return c.json({ ok: true, count: 0 });
|
||||
|
||||
(async () => {
|
||||
for (const row of rows) {
|
||||
try {
|
||||
await handOffToJellyfin(row.item_id);
|
||||
} catch (err) {
|
||||
warn(`verify-unverified: handoff for item ${row.item_id} threw: ${String(err)}`);
|
||||
}
|
||||
}
|
||||
log(`verify-unverified: processed ${rows.length} unverified done plan(s)`);
|
||||
})();
|
||||
|
||||
return c.json({ ok: true, count: rows.length });
|
||||
});
|
||||
|
||||
// ─── Stop running job ─────────────────────────────────────────────────────────
|
||||
|
||||
app.post("/stop", (c) => {
|
||||
|
||||
@@ -20,12 +20,24 @@ export function DoneColumn({ items, onMutate }: DoneColumnProps) {
|
||||
onMutate();
|
||||
};
|
||||
|
||||
const verifyUnverified = async () => {
|
||||
await api.post("/api/execute/verify-unverified");
|
||||
// Server processes sequentially in the background; each plan_update
|
||||
// SSE will trigger a pipeline reload as items get verified.
|
||||
};
|
||||
|
||||
const unverifiedCount = items.filter((i) => i.status === "done" && i.verified !== 1).length;
|
||||
|
||||
const actions = [];
|
||||
if (unverifiedCount > 0) {
|
||||
actions.push({ label: `Verify ${unverifiedCount}`, onClick: verifyUnverified });
|
||||
}
|
||||
if (items.length > 0) {
|
||||
actions.push({ label: "Clear", onClick: clear });
|
||||
}
|
||||
|
||||
return (
|
||||
<ColumnShell
|
||||
title="Done"
|
||||
count={items.length}
|
||||
actions={items.length > 0 ? [{ label: "Clear", onClick: clear }] : undefined}
|
||||
>
|
||||
<ColumnShell title="Done" count={items.length} actions={actions.length > 0 ? actions : undefined}>
|
||||
{items.map((item) => {
|
||||
const verified = item.status === "done" && item.verified === 1;
|
||||
const mark = verified ? "✓✓" : item.status === "done" ? "✓" : "✗";
|
||||
|
||||
Reference in New Issue
Block a user