From e40431462e7e05fc6e022154c54993ea45b8f81c Mon Sep 17 00:00:00 2001 From: Mikal Stordal Date: Thu, 17 Oct 2024 22:02:56 +0200 Subject: [PATCH] fix: scan all known libraries if series is gone - Scan all known libraries managed by the plugin if we receive one or more file removed events and the series is gone by the time we ask the server for the series related to the file. Since we won't be able to later ask the server for any series related metadata to generate the VFS path to check, so our next best bet is to just ask the core to scan the whole library to let the VFS generation logic take care of the removal of the file(s). --- Shokofin/Events/EventDispatchService.cs | 33 ++++++++++++++++++++----- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/Shokofin/Events/EventDispatchService.cs b/Shokofin/Events/EventDispatchService.cs index e8c0cfe4..91845b7b 100644 --- a/Shokofin/Events/EventDispatchService.cs +++ b/Shokofin/Events/EventDispatchService.cs @@ -281,6 +281,19 @@ private async Task ProcessFileEvents(int fileId, List<(UpdateReason Reason, int } // Something was removed, so assume the location is gone. else if (changes.FirstOrDefault(t => t.Reason is UpdateReason.Removed).Event is IFileEventArgs firstRemovedEvent) { + // If we don't know which series to remove, then add all of them to be scanned. + if (seriesIds.Count is 0) { + Logger.LogTrace("No series found for file. Adding all libraries. (File={FileId})", fileId); + foreach (var (vfsPath, mainMediaFolderPath, collectionType, mediaConfigs) in libraries) { + // Give the core logic _any_ file or folder placed directly in the media folder, so it will schedule the media folder to be refreshed. + var fileOrFolder = FileSystem.GetFileSystemEntryPaths(mainMediaFolderPath, false).FirstOrDefault(); + if (!string.IsNullOrEmpty(fileOrFolder)) + mediaFoldersToNotify.TryAdd(mainMediaFolderPath, (fileOrFolder, mainMediaFolderPath.GetFolderForPath())); + } + + goto aLabelToReduceNesting; + } + Logger.LogTrace("Processing file removed. (File={FileId})", fileId); relativePath = firstRemovedEvent.RelativePath; importFolderId = firstRemovedEvent.ImportFolderId; @@ -351,6 +364,7 @@ private async Task ProcessFileEvents(int fileId, List<(UpdateReason Reason, int } } + aLabelToReduceNesting:; if (LibraryScanWatcher.IsScanRunning) { Logger.LogDebug("Skipped notifying Jellyfin about {LocationCount} changes because a library scan is running. (File={FileId})", locationsToNotify.Count, fileId.ToString()); return; @@ -362,6 +376,7 @@ private async Task ProcessFileEvents(int fileId, List<(UpdateReason Reason, int LibraryMonitor.ReportFileSystemChanged(location); if (mediaFoldersToNotify.Count > 0) await Task.WhenAll(mediaFoldersToNotify.Values.Select(tuple => ReportMediaFolderChanged(tuple.mediaFolder, tuple.pathToReport))).ConfigureAwait(false); + Logger.LogDebug("Notified Jellyfin about {LocationCount} changes. (File={FileId})", locationsToNotify.Count + mediaFoldersToNotify.Count, fileId.ToString()); } catch (Exception ex) { Logger.LogError(ex, "Error processing {EventCount} file change events. (File={FileId})", changes.Count, fileId); @@ -397,15 +412,21 @@ private async Task> GetSeriesIdsForFile(int fileId, IFileEv var filteredSeriesIds = new HashSet(); foreach (var seriesId in seriesIds) { - var (primaryId, extraIds) = await ApiManager.GetSeriesIdsForSeason(seriesId); - var seriesPathSet = await ApiManager.GetPathSetForSeries(primaryId, extraIds); - if (seriesPathSet.Count > 0) { - filteredSeriesIds.Add(seriesId); + try { + var (primaryId, extraIds) = await ApiManager.GetSeriesIdsForSeason(seriesId); + var seriesPathSet = await ApiManager.GetPathSetForSeries(primaryId, extraIds); + if (seriesPathSet.Count > 0) { + filteredSeriesIds.Add(seriesId); + } + } + // If we fail to find the series data (most likely because it's already gone) then just abort early. We'll handle it elsewhere. + catch (ApiException ex) when (ex.StatusCode is System.Net.HttpStatusCode.NotFound) { + return new HashSet(); } } // Return all series if we only have this file for all of them, - // otherwise return only the series were we have other files that are + // otherwise return only the series where we have other files that are // not linked to other series. return filteredSeriesIds.Count is 0 ? seriesIds : filteredSeriesIds; } @@ -434,7 +455,7 @@ private async Task> GetSeriesIdsForFile(int fileId, IFileEv private void RemoveSymbolicLink(string filePath) { - // TODO: If this works better, the move it to an utility and also use it in the VFS if needed, or remove this comment if it's not needed. + // TODO: If this works better, then move it to an utility and also use it in the VFS if needed, or remove this comment if that's not needed. try { var fileExists = File.Exists(filePath); var fileInfo = new System.IO.FileInfo(filePath);