From 9700302964abacaf45aee45ba694e13f6640b225 Mon Sep 17 00:00:00 2001 From: Nav Date: Sat, 29 Aug 2026 02:50:29 +0000 Subject: [PATCH] feat(logging): emit integrity_verifying event when a file probe starts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Log an integrity_verifying activity row (and SSE push) before ffprobe/ffmpeg runs, so the live log shows verification in progress, not just the result - Gated behind the existing integrity_log_checks toggle - Series title passed through to ensureUpcomingEpisodes so rows carry the show - UI: Verifying… badge, muted style, log filter entries for Verifying / Integrity OK / Corrupt File --- src/public/index.html | 6 +++++- src/sonarr.js | 19 +++++++++++++++++-- src/watcher.js | 2 +- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/public/index.html b/src/public/index.html index 2d23771..ccce0c5 100644 --- a/src/public/index.html +++ b/src/public/index.html @@ -49,6 +49,7 @@ .badge-episode_remediated{background:rgba(239,68,68,.2);color:var(--red);font-weight:700;} .badge-episode_corrupt{background:rgba(249,115,22,.2);color:var(--orange);font-weight:700;} .badge-integrity_checked{background:rgba(59,130,246,.15);color:var(--blue);} + .badge-integrity_verifying{background:rgba(59,130,246,.08);color:var(--blue);opacity:.75;} .activity{background:var(--surface);border:1px solid var(--border);border-radius:10px;overflow:hidden;} .activity table{width:100%;border-collapse:collapse;} .activity th{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.8px;color:var(--muted);padding:10px 16px;text-align:left;border-bottom:1px solid var(--border);} @@ -199,6 +200,9 @@

EpisodeGuardGrabbed + + + @@ -484,7 +488,7 @@

EpisodeGuard/g,'>');} diff --git a/src/sonarr.js b/src/sonarr.js index 15866cf..1d29cd3 100644 --- a/src/sonarr.js +++ b/src/sonarr.js @@ -1,5 +1,5 @@ import { env } from './config.js'; -import { getSetting, isEpisodeFileVerified, markEpisodeFileVerified, clearVerifiedFile } from './db.js'; +import { getSetting, logEvent, isEpisodeFileVerified, markEpisodeFileVerified, clearVerifiedFile } from './db.js'; import { resolveLocalPath, probeMediaFile, evaluateIntegrity } from './prober.js'; async function sonarrReq(method, path, body) { @@ -88,7 +88,7 @@ export async function getTwoSeasonEpisodes(seriesId, season) { } -export async function ensureUpcomingEpisodes(seriesId, season, episode, preloaded, queuedEpisodeIds = new Set()) { +export async function ensureUpcomingEpisodes(seriesId, season, episode, preloaded, queuedEpisodeIds = new Set(), seriesTitle = null) { const lookahead = parseInt(getSetting('lookahead_episodes'), 10); const allEps = preloaded ? [...preloaded.current, ...preloaded.next] @@ -143,6 +143,21 @@ export async function ensureUpcomingEpisodes(seriesId, season, episode, preloade console.warn(`[sonarr] Could not fetch episode file ${ep.episodeFileId}:`, err.message); } + const verifyingDetails = { + message: 'Verifying file integrity (ffprobe + tail decode)', + file: epFile?.path || null, + }; + if (logIntegrityChecks) { + logEvent({ + event_type: 'integrity_verifying', + show_title: seriesTitle || null, + season: ep.seasonNumber, + episode: ep.episodeNumber, + details: verifyingDetails, + }); + } + console.log(`[integrity] Verifying S${pad(ep.seasonNumber)}E${pad(ep.episodeNumber)}: ${epFile?.path || '(no file path)'}`); + // Determine expected runtime from episode or series (minutes to seconds) const expectedMinutes = ep.runtime || (epFile && epFile.mediaInfo && epFile.mediaInfo.runTime) || null; const expectedRuntimeSec = expectedMinutes ? expectedMinutes * 60 : null; diff --git a/src/watcher.js b/src/watcher.js index f4387b6..a191796 100644 --- a/src/watcher.js +++ b/src/watcher.js @@ -198,7 +198,7 @@ async function processEpisode(ep, series, trigger, queuedEpisodeIds = new Set()) // 1. Ensure upcoming episodes console.log('[watcher] Checking upcoming for ' + label); - const result = await ensureUpcomingEpisodes(series.id, season, episode, preloaded, queuedEpisodeIds); + const result = await ensureUpcomingEpisodes(series.id, season, episode, preloaded, queuedEpisodeIds, series.title); // Log one row per upcoming episode action for (const a of result.actions) {