drop review_plans.verified column and all its references
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -327,7 +327,7 @@ app.get("/pipeline", (c) => {
|
||||
const done = db
|
||||
.prepare(`
|
||||
SELECT j.*, mi.name, mi.series_name, mi.type,
|
||||
rp.job_type, rp.apple_compat, rp.verified
|
||||
rp.job_type, rp.apple_compat
|
||||
FROM jobs j
|
||||
JOIN media_items mi ON mi.id = j.item_id
|
||||
JOIN review_plans rp ON rp.item_id = j.item_id
|
||||
@@ -770,7 +770,7 @@ app.post("/:id/reopen", (c) => {
|
||||
db.transaction(() => {
|
||||
// Leave plan.notes alone so the user keeps any ffmpeg error summary
|
||||
// from the prior run — useful context when redeciding decisions.
|
||||
db.prepare("UPDATE review_plans SET status = 'pending', verified = 0, reviewed_at = NULL WHERE id = ?").run(plan.id);
|
||||
db.prepare("UPDATE review_plans SET status = 'pending', reviewed_at = NULL WHERE id = ?").run(plan.id);
|
||||
db.prepare("DELETE FROM jobs WHERE item_id = ? AND status IN ('done', 'error')").run(id);
|
||||
})();
|
||||
return c.json({ ok: true });
|
||||
|
||||
@@ -78,6 +78,7 @@ function migrate(db: Database): void {
|
||||
// signal would come from our own ffprobe, not from a Jellyfin webhook.
|
||||
// RENAME COLUMN preserves values; both alters are no-ops on fresh DBs.
|
||||
alter("ALTER TABLE review_plans RENAME COLUMN webhook_verified TO verified");
|
||||
alter("ALTER TABLE review_plans DROP COLUMN verified");
|
||||
}
|
||||
|
||||
function seedDefaults(db: Database): void {
|
||||
|
||||
@@ -70,11 +70,6 @@ CREATE TABLE IF NOT EXISTS review_plans (
|
||||
subs_extracted INTEGER NOT NULL DEFAULT 0,
|
||||
notes TEXT,
|
||||
reviewed_at TEXT,
|
||||
-- An independent post-hoc check has confirmed the on-disk file matches
|
||||
-- the plan: either the analyzer saw is_noop=1 on first scan, or after
|
||||
-- a job completed we ffprobed the output file and it agreed with the
|
||||
-- kept/removed stream decisions. Surfaces as the ✓✓ in the Done column.
|
||||
verified INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
|
||||
@@ -182,61 +182,3 @@ describe("processWebhookEvent — done-status override", () => {
|
||||
expect(planStatusFor(db, fresh.Id)).toBe("pending");
|
||||
});
|
||||
});
|
||||
|
||||
describe("processWebhookEvent — webhook_verified flag", () => {
|
||||
beforeEach(() => _resetDedupe());
|
||||
|
||||
async function runWebhook(db: Database, item: JellyfinItem, cfg: RescanConfig = RESCAN_CFG) {
|
||||
return processWebhookEvent(
|
||||
{ NotificationType: "ItemAdded", ItemId: item.Id, ItemType: item.Type as "Movie" | "Episode" },
|
||||
{ db, jellyfin: JF, rescanCfg: cfg, getItemFn: async () => item },
|
||||
);
|
||||
}
|
||||
|
||||
function verifiedFor(db: Database, jellyfinId: string): number {
|
||||
return (
|
||||
db
|
||||
.prepare(
|
||||
"SELECT rp.verified as v FROM review_plans rp JOIN media_items mi ON mi.id = rp.item_id WHERE mi.jellyfin_id = ?",
|
||||
)
|
||||
.get(jellyfinId) as { v: number }
|
||||
).v;
|
||||
}
|
||||
|
||||
test("is_noop=1 on first scan sets webhook_verified=1 (no Jellyfin round-trip needed)", async () => {
|
||||
const db = makeDb();
|
||||
const fresh = fakeItem();
|
||||
await runWebhook(db, fresh);
|
||||
expect(verifiedFor(db, fresh.Id)).toBe(1);
|
||||
});
|
||||
|
||||
test("a post-execute webhook that still says is_noop=1 keeps webhook_verified=1", async () => {
|
||||
const db = makeDb();
|
||||
const fresh = fakeItem();
|
||||
await runWebhook(db, fresh);
|
||||
_resetDedupe();
|
||||
await runWebhook(db, fresh);
|
||||
expect(verifiedFor(db, fresh.Id)).toBe(1);
|
||||
});
|
||||
|
||||
test("webhook that flips plan off-noop clears webhook_verified back to 0", async () => {
|
||||
const db = makeDb();
|
||||
const noopItem = fakeItem();
|
||||
await runWebhook(db, noopItem);
|
||||
expect(verifiedFor(db, noopItem.Id)).toBe(1);
|
||||
|
||||
// Second probe: Jellyfin reports a drifted file (extra french track
|
||||
// that the 'deu' language config would now remove → is_noop=0).
|
||||
const driftedCfg: RescanConfig = { ...RESCAN_CFG, audioLanguages: ["deu"] };
|
||||
const drifted = fakeItem({
|
||||
MediaStreams: [
|
||||
{ Type: "Video", Index: 0, Codec: "h264" },
|
||||
{ Type: "Audio", Index: 1, Codec: "aac", Language: "eng", IsDefault: true },
|
||||
{ Type: "Audio", Index: 2, Codec: "aac", Language: "fra" },
|
||||
],
|
||||
});
|
||||
_resetDedupe();
|
||||
await runWebhook(db, drifted, driftedCfg);
|
||||
expect(verifiedFor(db, noopItem.Id)).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -229,17 +229,10 @@ export async function upsertJellyfinItem(
|
||||
// commit that made done terminal)
|
||||
// error → pending (retry loop)
|
||||
// else keep current status
|
||||
//
|
||||
// `verified` tracks whether we have independent confirmation the file
|
||||
// matches the plan. Set to 1 whenever is_noop=1 on a fresh analysis
|
||||
// (an unchanged file is already in its desired end state). Post-
|
||||
// execute, execute.ts re-runs verifyDesiredState and flips this on
|
||||
// when ffprobe agrees. Cleared the moment a webhook says the file
|
||||
// drifted off-noop.
|
||||
db
|
||||
.prepare(`
|
||||
INSERT INTO review_plans (item_id, status, is_noop, confidence, apple_compat, job_type, notes, verified)
|
||||
VALUES (?, 'pending', ?, ?, ?, ?, ?, ?)
|
||||
INSERT INTO review_plans (item_id, status, is_noop, confidence, apple_compat, job_type, notes)
|
||||
VALUES (?, 'pending', ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(item_id) DO UPDATE SET
|
||||
status = CASE
|
||||
WHEN excluded.is_noop = 1 THEN 'done'
|
||||
@@ -252,12 +245,7 @@ export async function upsertJellyfinItem(
|
||||
confidence = excluded.confidence,
|
||||
apple_compat = excluded.apple_compat,
|
||||
job_type = excluded.job_type,
|
||||
notes = excluded.notes,
|
||||
verified = CASE
|
||||
WHEN excluded.is_noop = 1 THEN 1
|
||||
WHEN ? = 'webhook' THEN 0
|
||||
ELSE review_plans.verified
|
||||
END
|
||||
notes = excluded.notes
|
||||
`)
|
||||
.run(
|
||||
itemId,
|
||||
@@ -266,9 +254,7 @@ export async function upsertJellyfinItem(
|
||||
analysis.apple_compat,
|
||||
analysis.job_type,
|
||||
analysis.notes.length > 0 ? analysis.notes.join("\n") : null,
|
||||
analysis.is_noop ? 1 : 0,
|
||||
source,
|
||||
source,
|
||||
source, // for the CASE WHEN ? = 'webhook' branch
|
||||
);
|
||||
|
||||
const planRow = db.prepare("SELECT id FROM review_plans WHERE item_id = ?").get(itemId) as { id: number };
|
||||
|
||||
@@ -65,7 +65,6 @@ export interface ReviewPlan {
|
||||
subs_extracted: number;
|
||||
notes: string | null;
|
||||
reviewed_at: string | null;
|
||||
verified: number;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user