store confidence, apple_compat, job_type, transcode_codec during scan

This commit is contained in:
2026-03-27 01:45:56 +01:00
parent b1cf0fca38
commit ecb0732185

View File

@@ -164,14 +164,20 @@ async function runScan(limit: number | null = null): Promise<void> {
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
const upsertPlan = db.prepare(`
INSERT INTO review_plans (item_id, status, is_noop, notes)
VALUES (?, 'pending', ?, ?)
ON CONFLICT(item_id) DO UPDATE SET is_noop = excluded.is_noop, notes = excluded.notes
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 review_plans.status IN ('done','error') THEN 'pending' ELSE review_plans.status END,
is_noop = excluded.is_noop,
confidence = excluded.confidence,
apple_compat = excluded.apple_compat,
job_type = excluded.job_type,
notes = excluded.notes
`);
const upsertDecision = db.prepare(`
INSERT INTO stream_decisions (plan_id, stream_id, action, target_index)
VALUES (?, ?, ?, ?)
ON CONFLICT(plan_id, stream_id) DO UPDATE SET action = excluded.action, target_index = excluded.target_index
INSERT INTO stream_decisions (plan_id, stream_id, action, target_index, transcode_codec)
VALUES (?, ?, ?, ?, ?)
ON CONFLICT(plan_id, stream_id) DO UPDATE SET action = excluded.action, target_index = excluded.target_index, transcode_codec = excluded.transcode_codec
`);
const getItemByJellyfinId = db.prepare('SELECT id FROM media_items WHERE jellyfin_id = ?');
const getPlanByItemId = db.prepare('SELECT id FROM review_plans WHERE item_id = ?');
@@ -213,6 +219,16 @@ async function runScan(limit: number | null = null): Promise<void> {
if (lang) { if (origLang && normalizeLanguage(origLang) !== normalizeLanguage(lang)) needsReview = 1; origLang = lang; origLangSource = 'sonarr'; }
}
// Compute confidence from source agreement
let confidence: 'high' | 'low' = 'low';
if (!origLang) {
confidence = 'low'; // unknown language
} else if (needsReview) {
confidence = 'low'; // sources disagree
} else {
confidence = 'high'; // language known, no conflicts
}
upsertItem.run(
jellyfinItem.Id, jellyfinItem.Type === 'Episode' ? 'Episode' : 'Movie',
jellyfinItem.Name, jellyfinItem.SeriesName ?? null, jellyfinItem.SeriesId ?? null,
@@ -233,10 +249,12 @@ async function runScan(limit: number | null = null): Promise<void> {
}
const streams = getStreamsByItemId.all(itemId) as MediaStream[];
const analysis = analyzeItem({ original_language: origLang, needs_review: needsReview }, streams, { subtitleLanguages, audioLanguages });
upsertPlan.run(itemId, analysis.is_noop ? 1 : 0, analysis.notes);
const analysis = analyzeItem({ original_language: origLang, needs_review: needsReview, container: jellyfinItem.Container ?? null }, streams, { subtitleLanguages, audioLanguages });
// Override base confidence with scan-computed value
const finalConfidence = confidence;
upsertPlan.run(itemId, analysis.is_noop ? 1 : 0, finalConfidence, analysis.apple_compat, analysis.job_type, analysis.notes.length > 0 ? analysis.notes.join('\n') : null);
const planRow = getPlanByItemId.get(itemId) as { id: number };
for (const dec of analysis.decisions) upsertDecision.run(planRow.id, dec.stream_id, dec.action, dec.target_index);
for (const dec of analysis.decisions) upsertDecision.run(planRow.id, dec.stream_id, dec.action, dec.target_index, dec.transcode_codec);
emitSse('log', { name: jellyfinItem.Name, type: jellyfinItem.Type, status: 'scanned', file: jellyfinItem.Path });
} catch (err) {