private void RemoveObsoleteSeasons(Series series) { // TODO Legacy. It's not really "physical" seasons as any virtual seasons are always converted to non-virtual in FillInMissingSeasonsAsync. var physicalSeasonNumbers = new HashSet <int>(); var virtualSeasons = new List <Season>(); foreach (var existingSeason in series.Children.OfType <Season>()) { if (existingSeason.LocationType != LocationType.Virtual && existingSeason.IndexNumber.HasValue) { physicalSeasonNumbers.Add(existingSeason.IndexNumber.Value); } else if (existingSeason.LocationType == LocationType.Virtual) { virtualSeasons.Add(existingSeason); } } foreach (var virtualSeason in virtualSeasons) { var seasonNumber = virtualSeason.IndexNumber; // If there's a physical season with the same number or no episodes in the season, delete it if ((seasonNumber.HasValue && physicalSeasonNumbers.Contains(seasonNumber.Value)) || !virtualSeason.GetEpisodes().Any()) { Logger.LogInformation("Removing virtual season {SeasonNumber} in series {SeriesName}", virtualSeason.IndexNumber, series.Name); LibraryManager.DeleteItem( virtualSeason, new DeleteOptions { DeleteFileLocation = true }, false); } } }
/// <summary> /// Validates the children internal. /// </summary> /// <param name="progress">The progress.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="recursive">if set to <c>true</c> [recursive].</param> /// <param name="refreshChildMetadata">if set to <c>true</c> [refresh child metadata].</param> /// <param name="refreshOptions">The refresh options.</param> /// <param name="directoryService">The directory service.</param> /// <returns>Task.</returns> protected async virtual Task ValidateChildrenInternal(IProgress <double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService) { var locationType = LocationType; cancellationToken.ThrowIfCancellationRequested(); var validChildren = new List <BaseItem>(); if (locationType != LocationType.Remote && locationType != LocationType.Virtual) { IEnumerable <BaseItem> nonCachedChildren; try { nonCachedChildren = GetNonCachedChildren(directoryService); } catch (IOException ex) { nonCachedChildren = new BaseItem[] { }; Logger.ErrorException("Error getting file system entries for {0}", ex, Path); } if (nonCachedChildren == null) { return; //nothing to validate } progress.Report(5); //build a dictionary of the current children we have now by Id so we can compare quickly and easily var currentChildren = GetActualChildrenDictionary(); //create a list for our validated children var newItems = new List <BaseItem>(); cancellationToken.ThrowIfCancellationRequested(); foreach (var child in nonCachedChildren) { BaseItem currentChild; if (currentChildren.TryGetValue(child.Id, out currentChild) && IsValidFromResolver(currentChild, child)) { await currentChild.UpdateIsOffline(false).ConfigureAwait(false); validChildren.Add(currentChild); continue; } // Brand new item - needs to be added child.SetParent(this); newItems.Add(child); validChildren.Add(child); } // If any items were added or removed.... if (newItems.Count > 0 || currentChildren.Count != validChildren.Count) { // That's all the new and changed ones - now see if there are any that are missing var itemsRemoved = currentChildren.Values.Except(validChildren).ToList(); var actualRemovals = new List <BaseItem>(); foreach (var item in itemsRemoved) { var itemLocationType = item.LocationType; if (itemLocationType == LocationType.Virtual || itemLocationType == LocationType.Remote) { } else if (!string.IsNullOrEmpty(item.Path) && IsPathOffline(item.Path)) { await item.UpdateIsOffline(true).ConfigureAwait(false); } else { actualRemovals.Add(item); } } if (actualRemovals.Count > 0) { foreach (var item in actualRemovals) { Logger.Debug("Removed item: " + item.Path); item.SetParent(null); item.IsOffline = false; await LibraryManager.DeleteItem(item, new DeleteOptions { DeleteFileLocation = false }).ConfigureAwait(false); LibraryManager.ReportItemRemoved(item); } } await LibraryManager.CreateItems(newItems, cancellationToken).ConfigureAwait(false); } } progress.Report(10); cancellationToken.ThrowIfCancellationRequested(); if (recursive) { await ValidateSubFolders(ActualChildren.OfType <Folder>().ToList(), directoryService, progress, cancellationToken).ConfigureAwait(false); } progress.Report(20); if (refreshChildMetadata) { var container = this as IMetadataContainer; var innerProgress = new ActionableProgress <double>(); innerProgress.RegisterAction(p => progress.Report(.80 * p + 20)); if (container != null) { await container.RefreshAllMetadata(refreshOptions, innerProgress, cancellationToken).ConfigureAwait(false); } else { await RefreshMetadataRecursive(refreshOptions, recursive, innerProgress, cancellationToken); } } progress.Report(100); }