From 148e901f79adc3e72a24df7d6052c236753ed4a0 Mon Sep 17 00:00:00 2001 From: BobSilent Date: Fri, 19 Jan 2024 00:58:29 +0100 Subject: [PATCH] add delete of missing episodes in library scan --- .../Providers/TvdbMissingEpisodeProvider.cs | 47 ++++++++++++++++--- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/Jellyfin.Plugin.Tvdb/Providers/TvdbMissingEpisodeProvider.cs b/Jellyfin.Plugin.Tvdb/Providers/TvdbMissingEpisodeProvider.cs index 5ab0e51..1d6c76e 100644 --- a/Jellyfin.Plugin.Tvdb/Providers/TvdbMissingEpisodeProvider.cs +++ b/Jellyfin.Plugin.Tvdb/Providers/TvdbMissingEpisodeProvider.cs @@ -99,9 +99,20 @@ protected virtual void Dispose(bool disposing) private static bool EpisodeExists(EpisodeBaseRecord episodeRecord, IReadOnlyList existingEpisodes) { - return existingEpisodes.Any(ep => ep.ContainsEpisodeNumber(episodeRecord.Number) && ep.ParentIndexNumber == episodeRecord.Number); + return existingEpisodes.Any(ep => EpisodeEquals(episodeRecord, ep)); } + private static bool EpisodeEquals(EpisodeBaseRecord episodeRecord, Episode otherEpisodeRecord) + { + return otherEpisodeRecord.ContainsEpisodeNumber(episodeRecord.Number) + && otherEpisodeRecord.ParentIndexNumber == episodeRecord.Number; + } + + /// + /// Is Metadata fetcher enabled for Series, Season or Episode. + /// + /// Series, Season or Episode. + /// true if enabled. private bool IsEnabledForLibrary(BaseItem item) { Series? series = item switch @@ -202,13 +213,23 @@ private async Task HandleSeason(Season season) var tvdbId = Convert.ToInt32(tvdbIdTxt, CultureInfo.InvariantCulture); var allEpisodes = await GetAllEpisodes(tvdbId, season.GetPreferredMetadataLanguage()).ConfigureAwait(false); - var existingEpisodes = season.Children.OfType().ToList(); + var existingEpisodes = season.GetEpisodes().OfType().ToList(); for (var i = 0; i < allEpisodes.Count; i++) { var episode = allEpisodes[i]; - if (EpisodeExists(episode, existingEpisodes)) + var foundEpisodes = existingEpisodes.Where(e => EpisodeEquals(episode, e)).ToList(); + if (foundEpisodes.Any()) { + // So we have at least one existing episode for our episodeRecord + var physicalEpisodes = foundEpisodes.Where(e => !e.IsVirtualItem); + if (physicalEpisodes.Any()) + { + // if there is a physical episode we can delete existing virtual episode entries + var virtualEpisodes = foundEpisodes.Where(e => e.IsVirtualItem).ToList(); + DeleteVirtualItems(virtualEpisodes); + } + continue; } @@ -242,19 +263,31 @@ private void OnLibraryManagerItemUpdated(object? sender, ItemChangeEventArgs ite parentIndexNumber = itemChangeEventArgs.Item.ParentIndexNumber; } + var existingVirtualItems = GetVirtualItems(itemChangeEventArgs.Item, itemChangeEventArgs.Parent, indexNumber, parentIndexNumber); + DeleteVirtualItems(existingVirtualItems); + } + + private List GetVirtualItems(BaseItem item, BaseItem? parent, int? indexNumber, int? parentIndexNumber) + { var query = new InternalItemsQuery { IsVirtualItem = true, IndexNumber = indexNumber, ParentIndexNumber = parentIndexNumber, - IncludeItemTypes = new[] { itemChangeEventArgs.Item.GetBaseItemKind() }, - Parent = itemChangeEventArgs.Parent, + IncludeItemTypes = new[] { item.GetBaseItemKind() }, + Parent = parent, + Recursive = true, GroupByPresentationUniqueKey = false, DtoOptions = new DtoOptions(true) }; var existingVirtualItems = _libraryManager.GetItemList(query); + return existingVirtualItems; + } + private void DeleteVirtualItems(List existingVirtualItems) + where T : BaseItem + { var deleteOptions = new DeleteOptions { DeleteFileLocation = true @@ -263,7 +296,9 @@ private void OnLibraryManagerItemUpdated(object? sender, ItemChangeEventArgs ite // Remove the virtual season/episode that matches the newly updated item for (var i = 0; i < existingVirtualItems.Count; i++) { - _libraryManager.DeleteItem(existingVirtualItems[i], deleteOptions); + var currentItem = existingVirtualItems[i]; + _logger.LogDebug("Delete VirtualItem {Name} - S{Season}E{Episode}", currentItem.Name, currentItem.ParentIndexNumber, currentItem.IndexNumber); + _libraryManager.DeleteItem(currentItem, deleteOptions); } }