push verified=1 to the UI via a plan_update SSE event
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
the ✓✓ write was landing in the db but never reaching the browser. job_update fires once at job completion (card renders ✓, verified=0), then handOffToJellyfin takes ~15s to refresh jellyfin + re-analyze + UPDATE review_plans SET verified=1. no further sse, so the pipeline page never re-polled and the card stayed at ✓ until the user navigated away and back. new plan_update event emitted at the end of handOffToJellyfin. the pipeline page listens and triggers the same 1s-coalesced reload as job_update, so the done column promotes ✓ → ✓✓ within a second of jellyfin's verdict landing. 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",
|
"name": "netfelix-audio-fix",
|
||||||
"version": "2026.04.14.22",
|
"version": "2026.04.14.23",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev:server": "NODE_ENV=development bun --hot server/index.tsx",
|
"dev:server": "NODE_ENV=development bun --hot server/index.tsx",
|
||||||
"dev:client": "vite",
|
"dev:client": "vite",
|
||||||
|
|||||||
@@ -88,6 +88,10 @@ async function handOffToJellyfin(itemId: number): Promise<void> {
|
|||||||
|
|
||||||
const result = await upsertJellyfinItem(db, fresh, rescanCfg, { source: "webhook" });
|
const result = await upsertJellyfinItem(db, fresh, rescanCfg, { source: "webhook" });
|
||||||
log(`Post-job verify for item ${itemId}: is_noop=${result.isNoop}`);
|
log(`Post-job verify for item ${itemId}: is_noop=${result.isNoop}`);
|
||||||
|
// Nudge connected clients so the Done column re-polls and promotes
|
||||||
|
// the card from ✓ to ✓✓ (or flips it back to Review if jellyfin
|
||||||
|
// disagreed).
|
||||||
|
emitPlanUpdate(itemId);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
warn(`Post-job verification for item ${itemId} failed: ${String(err)}`);
|
warn(`Post-job verification for item ${itemId} failed: ${String(err)}`);
|
||||||
}
|
}
|
||||||
@@ -169,6 +173,18 @@ function emitJobProgress(jobId: number, seconds: number, total: number): void {
|
|||||||
for (const l of jobListeners) l(line);
|
for (const l of jobListeners) l(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit when a review_plan mutates asynchronously (after the job already
|
||||||
|
* finished). Right now the only producer is handOffToJellyfin — the
|
||||||
|
* verified=1 write that lands ~15s after a job completes. Without this
|
||||||
|
* the UI would keep showing ✓ indefinitely until the user navigates
|
||||||
|
* away and back, since we'd never fire job_update again for that item.
|
||||||
|
*/
|
||||||
|
function emitPlanUpdate(itemId: number): void {
|
||||||
|
const line = `event: plan_update\ndata: ${JSON.stringify({ itemId })}\n\n`;
|
||||||
|
for (const l of jobListeners) l(line);
|
||||||
|
}
|
||||||
|
|
||||||
/** Parse "Duration: HH:MM:SS.MS" from ffmpeg startup output. */
|
/** Parse "Duration: HH:MM:SS.MS" from ffmpeg startup output. */
|
||||||
function parseFFmpegDuration(line: string): number | null {
|
function parseFFmpegDuration(line: string): number | null {
|
||||||
const match = line.match(/Duration:\s*(\d+):(\d+):(\d+)\.(\d+)/);
|
const match = line.match(/Duration:\s*(\d+):(\d+):(\d+)\.(\d+)/);
|
||||||
|
|||||||
@@ -64,6 +64,12 @@ export function PipelinePage() {
|
|||||||
}
|
}
|
||||||
scheduleReload();
|
scheduleReload();
|
||||||
});
|
});
|
||||||
|
// plan_update lands ~15s after a job finishes — the post-job jellyfin
|
||||||
|
// verification writes verified=1 (or flips the plan back to pending).
|
||||||
|
// Without refreshing here the Done column would never promote ✓ to ✓✓.
|
||||||
|
es.addEventListener("plan_update", () => {
|
||||||
|
scheduleReload();
|
||||||
|
});
|
||||||
es.addEventListener("job_progress", (e) => {
|
es.addEventListener("job_progress", (e) => {
|
||||||
setProgress(JSON.parse((e as MessageEvent).data));
|
setProgress(JSON.parse((e as MessageEvent).data));
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user