From 13471e21fba99d597b3fd9b8fcbe4b98e04252ab Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Sat, 4 Jul 2026 21:42:43 +0200 Subject: [PATCH] fix bug that causesd stale media (not on disk) to be suggested to workers --- .../Controllers/CoverageController.cs | 2 +- .../Controllers/TasksController.cs | 34 ++++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/Jellyfin.Plugin.JRay/Controllers/CoverageController.cs b/Jellyfin.Plugin.JRay/Controllers/CoverageController.cs index 1459fea..7e65db3 100644 --- a/Jellyfin.Plugin.JRay/Controllers/CoverageController.cs +++ b/Jellyfin.Plugin.JRay/Controllers/CoverageController.cs @@ -64,7 +64,7 @@ public class CoverageController : ControllerBase foreach (var item in items) { - if (string.IsNullOrEmpty(item.Path)) + if (!TasksController.MediaFileExists(item)) { continue; } diff --git a/Jellyfin.Plugin.JRay/Controllers/TasksController.cs b/Jellyfin.Plugin.JRay/Controllers/TasksController.cs index 73b2a5f..1f2e6cd 100644 --- a/Jellyfin.Plugin.JRay/Controllers/TasksController.cs +++ b/Jellyfin.Plugin.JRay/Controllers/TasksController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using Jellyfin.Data.Enums; using Jellyfin.Plugin.JRay.Models; @@ -66,7 +67,7 @@ public class TasksController : ControllerBase // drop anything ignored, and order prioritised items ahead of the rest. // Randomise within each tier so the backlog still spreads across workers. var pending = items - .Where(item => !string.IsNullOrEmpty(item.Path) && !_truthDataService.HasTruth(item.Id, item.Path)) + .Where(item => MediaFileExists(item) && !_truthDataService.HasTruth(item.Id, item.Path)) .Select(item => new { Item = item, @@ -96,4 +97,35 @@ public class TasksController : ControllerBase { return item is Episode episode ? episode.SeriesId : Guid.Empty; } + + /// + /// Determines whether an item's backing media still exists on disk. + /// + /// A library item can outlive its file: deleting media from disk does not + /// always purge the Jellyfin database entry, and such ghosts keep + /// IsVirtualItem == false, so filtering on that flag alone is not + /// enough. Without this check the pending endpoint hands stale paths/URLs to + /// extraction workers, and — because a missing file can never gain truth + /// data — those items stay pending forever. + /// + /// Only local (file-protocol) items are stat-checked; remote/streamed items + /// are assumed present so we never stat something off-box. + /// + /// The library item. + /// true if the item has usable backing media; otherwise false. + internal static bool MediaFileExists(BaseItem item) + { + if (string.IsNullOrEmpty(item.Path)) + { + return false; + } + + if (!item.IsFileProtocol) + { + return true; + } + + // Path may point at a file or, for folder-based media, a directory. + return System.IO.File.Exists(item.Path) || Directory.Exists(item.Path); + } }