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) {