diff options
| author | Joshua M. Boniface <joshua@boniface.me> | 2025-08-03 17:27:17 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-08-03 17:27:17 -0400 |
| commit | 4b6fb6c4bb2478badad068ce18aabe0c2955db48 (patch) | |
| tree | 15f986ee62327cceb8f5c8f009bcf08d10cfaa66 /MediaBrowser.Controller/Entities | |
| parent | e7bc86ebb8496615e0b3f73eb4f13ab4c0913dc8 (diff) | |
| parent | db7465e83d9cc07134a0bffad7ed17b1c7b873da (diff) | |
Merge branch 'master' into master
Diffstat (limited to 'MediaBrowser.Controller/Entities')
| -rw-r--r-- | MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs | 4 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/Audio/MusicArtist.cs | 10 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/BaseItem.cs | 133 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/BaseItemExtensions.cs | 14 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/CollectionFolder.cs | 2 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/Folder.cs | 101 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/InternalItemsQuery.cs | 37 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/InternalPeopleQuery.cs | 2 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/Movies/BoxSet.cs | 6 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/PeopleHelper.cs | 2 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/TV/Episode.cs | 39 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/TV/Season.cs | 22 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/TV/Series.cs | 48 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/UserRootFolder.cs | 4 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/UserView.cs | 2 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Entities/UserViewBuilder.cs | 21 |
16 files changed, 286 insertions, 161 deletions
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs index f3873775b..d016d8f62 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs @@ -8,8 +8,10 @@ using System.Linq; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Data.Entities; +using Jellyfin.Data; using Jellyfin.Data.Enums; +using Jellyfin.Database.Implementations.Entities; +using Jellyfin.Database.Implementations.Enums; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index ecb3ac3a6..58841e5b7 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -8,8 +8,10 @@ using System.Linq; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Data.Entities; +using Jellyfin.Data; using Jellyfin.Data.Enums; +using Jellyfin.Database.Implementations.Entities; +using Jellyfin.Database.Implementations.Enums; using Jellyfin.Extensions; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; @@ -138,11 +140,9 @@ namespace MediaBrowser.Controller.Entities.Audio private static List<string> GetUserDataKeys(MusicArtist item) { var list = new List<string>(); - var id = item.GetProviderId(MetadataProvider.MusicBrainzArtist); - - if (!string.IsNullOrEmpty(id)) + if (item.TryGetProviderId(MetadataProvider.MusicBrainzArtist, out var externalId)) { - list.Add("Artist-Musicbrainz-" + id); + list.Add("Artist-Musicbrainz-" + externalId); } list.Add("Artist-" + (item.Name ?? string.Empty).RemoveDiacritics()); diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 55553da49..bb0b26b8e 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -12,8 +12,10 @@ using System.Text; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Data.Entities; +using Jellyfin.Data; using Jellyfin.Data.Enums; +using Jellyfin.Database.Implementations.Entities; +using Jellyfin.Database.Implementations.Enums; using Jellyfin.Extensions; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Channels; @@ -21,7 +23,9 @@ using MediaBrowser.Controller.Chapters; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.MediaSegments; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Dto; @@ -31,7 +35,6 @@ using MediaBrowser.Model.IO; using MediaBrowser.Model.Library; using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.MediaInfo; -using MediaBrowser.Model.Providers; using Microsoft.Extensions.Logging; namespace MediaBrowser.Controller.Entities @@ -49,7 +52,7 @@ namespace MediaBrowser.Controller.Entities /// The supported image extensions. /// </summary> public static readonly string[] SupportedImageExtensions - = new[] { ".png", ".jpg", ".jpeg", ".webp", ".tbn", ".gif", ".svg" }; + = [".png", ".jpg", ".jpeg", ".webp", ".tbn", ".gif", ".svg"]; private static readonly List<string> _supportedExtensions = new List<string>(SupportedImageExtensions) { @@ -445,7 +448,7 @@ namespace MediaBrowser.Controller.Entities return Array.Empty<string>(); } - return new[] { Path }; + return [Path]; } } @@ -481,7 +484,7 @@ namespace MediaBrowser.Controller.Entities public static IItemRepository ItemRepository { get; set; } - public static IChapterRepository ChapterRepository { get; set; } + public static IChapterManager ChapterManager { get; set; } public static IFileSystem FileSystem { get; set; } @@ -578,6 +581,9 @@ namespace MediaBrowser.Controller.Entities [JsonIgnore] public int? InheritedParentalRatingValue { get; set; } + [JsonIgnore] + public int? InheritedParentalRatingSubValue { get; set; } + /// <summary> /// Gets or sets the critic rating. /// </summary> @@ -919,7 +925,7 @@ namespace MediaBrowser.Controller.Entities // Remove from middle if surrounded by spaces sortable = sortable.Replace(" " + search + " ", " ", StringComparison.Ordinal); - // Remove from end if followed by a space + // Remove from end if preceeded by a space if (sortable.EndsWith(" " + search, StringComparison.Ordinal)) { sortable = sortable.Remove(sortable.Length - (search.Length + 1)); @@ -1260,7 +1266,7 @@ namespace MediaBrowser.Controller.Entities } /// <summary> - /// Overrides the base implementation to refresh metadata for local trailers. + /// The base implementation to refresh metadata. /// </summary> /// <param name="options">The options.</param> /// <param name="cancellationToken">The cancellation token.</param> @@ -1357,9 +1363,7 @@ namespace MediaBrowser.Controller.Entities protected virtual FileSystemMetadata[] GetFileSystemChildren(IDirectoryService directoryService) { - var path = ContainingFolderPath; - - return directoryService.GetFileSystemEntries(path); + return directoryService.GetFileSystemEntries(ContainingFolderPath); } private async Task<bool> RefreshExtras(BaseItem item, MetadataRefreshOptions options, IReadOnlyList<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken) @@ -1388,6 +1392,23 @@ namespace MediaBrowser.Controller.Entities return RefreshMetadataForOwnedItem(i, true, subOptions, cancellationToken); }); + // Cleanup removed extras + var removedExtraIds = item.ExtraIds.Where(e => !newExtraIds.Contains(e)).ToArray(); + if (removedExtraIds.Length > 0) + { + var removedExtras = LibraryManager.GetItemList(new InternalItemsQuery() + { + ItemIds = removedExtraIds + }); + foreach (var removedExtra in removedExtras) + { + LibraryManager.DeleteItem(removedExtra, new DeleteOptions() + { + DeleteFileLocation = false + }); + } + } + await Task.WhenAll(tasks).ConfigureAwait(false); item.ExtraIds = newExtraIds; @@ -1402,7 +1423,14 @@ namespace MediaBrowser.Controller.Entities public virtual bool RequiresRefresh() { - return false; + if (string.IsNullOrEmpty(Path) || DateModified == DateTime.MinValue) + { + return false; + } + + var info = FileSystem.GetFileSystemInfo(Path); + + return info.Exists && this.HasChanged(info.LastWriteTimeUtc); } public virtual List<string> GetUserDataKeys() @@ -1537,7 +1565,8 @@ namespace MediaBrowser.Controller.Entities return false; } - var maxAllowedRating = user.MaxParentalAgeRating; + var maxAllowedRating = user.MaxParentalRatingScore; + var maxAllowedSubRating = user.MaxParentalRatingSubScore; var rating = CustomRatingForComparison; if (string.IsNullOrEmpty(rating)) @@ -1551,10 +1580,10 @@ namespace MediaBrowser.Controller.Entities return !GetBlockUnratedValue(user); } - var value = LocalizationManager.GetRatingLevel(rating); + var ratingScore = LocalizationManager.GetRatingScore(rating); // Could not determine rating level - if (!value.HasValue) + if (ratingScore is null) { var isAllowed = !GetBlockUnratedValue(user); @@ -1566,10 +1595,15 @@ namespace MediaBrowser.Controller.Entities return isAllowed; } - return !maxAllowedRating.HasValue || value.Value <= maxAllowedRating.Value; + if (maxAllowedSubRating is not null) + { + return (ratingScore.SubScore ?? 0) <= maxAllowedSubRating && ratingScore.Score <= maxAllowedRating.Value; + } + + return !maxAllowedRating.HasValue || ratingScore.Score <= maxAllowedRating.Value; } - public int? GetInheritedParentalRatingValue() + public ParentalRatingScore GetParentalRatingScore() { var rating = CustomRatingForComparison; @@ -1583,7 +1617,7 @@ namespace MediaBrowser.Controller.Entities return null; } - return LocalizationManager.GetRatingLevel(rating); + return LocalizationManager.GetRatingScore(rating); } public List<string> GetInheritedTags() @@ -1681,7 +1715,7 @@ namespace MediaBrowser.Controller.Entities public virtual string GetClientTypeName() { - if (IsFolder && SourceType == SourceType.Channel && this is not Channel) + if (IsFolder && SourceType == SourceType.Channel && this is not Channel && this is not Season && this is not Series) { return "ChannelFolderItem"; } @@ -1775,7 +1809,6 @@ namespace MediaBrowser.Controller.Entities public void AddStudio(string name) { ArgumentException.ThrowIfNullOrEmpty(name); - var current = Studios; if (!current.Contains(name, StringComparison.OrdinalIgnoreCase)) @@ -1794,7 +1827,7 @@ namespace MediaBrowser.Controller.Entities public void SetStudios(IEnumerable<string> names) { - Studios = names.Distinct().ToArray(); + Studios = names.Trimmed().Distinct(StringComparer.OrdinalIgnoreCase).ToArray(); } /// <summary> @@ -1960,9 +1993,10 @@ namespace MediaBrowser.Controller.Entities } // Remove from file system - if (info.IsLocalFile) + var path = info.Path; + if (info.IsLocalFile && !string.IsNullOrWhiteSpace(path)) { - FileSystem.DeleteFile(info.Path); + FileSystem.DeleteFile(path); } // Remove from item @@ -1973,7 +2007,7 @@ namespace MediaBrowser.Controller.Entities public void RemoveImage(ItemImageInfo image) { - RemoveImages(new[] { image }); + RemoveImages([image]); } public void RemoveImages(IEnumerable<ItemImageInfo> deletedImages) @@ -2008,7 +2042,7 @@ namespace MediaBrowser.Controller.Entities continue; } - (deletedImages ??= new List<ItemImageInfo>()).Add(imageInfo); + (deletedImages ??= []).Add(imageInfo); } var anyImagesRemoved = deletedImages?.Count > 0; @@ -2040,7 +2074,7 @@ namespace MediaBrowser.Controller.Entities { if (imageType == ImageType.Chapter) { - var chapter = ChapterRepository.GetChapter(this.Id, imageIndex); + var chapter = ChapterManager.GetChapter(Id, imageIndex); if (chapter is null) { @@ -2090,7 +2124,7 @@ namespace MediaBrowser.Controller.Entities if (image.Type == ImageType.Chapter) { - var chapters = ChapterRepository.GetChapters(this.Id); + var chapters = ChapterManager.GetChapters(Id); for (var i = 0; i < chapters.Count; i++) { if (chapters[i].ImagePath == image.Path) @@ -2211,11 +2245,7 @@ namespace MediaBrowser.Controller.Entities { return new[] { - new FileSystemMetadata - { - FullName = Path, - IsDirectory = IsFolder - } + FileSystem.GetFileSystemInfo(Path) }.Concat(GetLocalMetadataFilesToDelete()); } @@ -2223,7 +2253,7 @@ namespace MediaBrowser.Controller.Entities { if (IsFolder || !IsInMixedFolder) { - return new List<FileSystemMetadata>(); + return []; } var filename = System.IO.Path.GetFileNameWithoutExtension(Path); @@ -2479,10 +2509,10 @@ namespace MediaBrowser.Controller.Entities protected virtual List<string> GetEtagValues(User user) { - return new List<string> - { + return + [ DateLastSaved.Ticks.ToString(CultureInfo.InvariantCulture) - }; + ]; } public virtual IEnumerable<Guid> GetAncestorIds() @@ -2502,7 +2532,7 @@ namespace MediaBrowser.Controller.Entities public virtual IEnumerable<Guid> GetIdsForAncestorQuery() { - return new[] { Id }; + return [Id]; } public virtual double? GetRefreshProgress() @@ -2516,11 +2546,29 @@ namespace MediaBrowser.Controller.Entities var item = this; - var inheritedParentalRatingValue = item.GetInheritedParentalRatingValue() ?? null; - if (inheritedParentalRatingValue != item.InheritedParentalRatingValue) + var rating = item.GetParentalRatingScore(); + if (rating is not null) { - item.InheritedParentalRatingValue = inheritedParentalRatingValue; - updateType |= ItemUpdateType.MetadataImport; + if (rating.Score != item.InheritedParentalRatingValue) + { + item.InheritedParentalRatingValue = rating.Score; + updateType |= ItemUpdateType.MetadataImport; + } + + if (rating.SubScore != item.InheritedParentalRatingSubValue) + { + item.InheritedParentalRatingSubValue = rating.SubScore; + updateType |= ItemUpdateType.MetadataImport; + } + } + else + { + if (item.InheritedParentalRatingValue is not null) + { + item.InheritedParentalRatingValue = null; + item.InheritedParentalRatingSubValue = null; + updateType |= ItemUpdateType.MetadataImport; + } } return updateType; @@ -2540,8 +2588,9 @@ namespace MediaBrowser.Controller.Entities .Select(i => i.OfficialRating) .Where(i => !string.IsNullOrEmpty(i)) .Distinct(StringComparer.OrdinalIgnoreCase) - .Select(rating => (rating, LocalizationManager.GetRatingLevel(rating))) - .OrderBy(i => i.Item2 ?? 1000) + .Select(rating => (rating, LocalizationManager.GetRatingScore(rating))) + .OrderBy(i => i.Item2 is null ? 1001 : i.Item2.Score) + .ThenBy(i => i.Item2 is null ? 1001 : i.Item2.SubScore) .Select(i => i.rating); OfficialRating = ratings.FirstOrDefault() ?? currentOfficialRating; diff --git a/MediaBrowser.Controller/Entities/BaseItemExtensions.cs b/MediaBrowser.Controller/Entities/BaseItemExtensions.cs index dcd22a3b4..668e2c1e2 100644 --- a/MediaBrowser.Controller/Entities/BaseItemExtensions.cs +++ b/MediaBrowser.Controller/Entities/BaseItemExtensions.cs @@ -114,5 +114,19 @@ namespace MediaBrowser.Controller.Entities source.DeepCopy(dest); return dest; } + + /// <summary> + /// Determines if the item has changed. + /// </summary> + /// <param name="source">The source object.</param> + /// <param name="asOf">The timestamp to detect changes as of.</param> + /// <typeparam name="T">Source type.</typeparam> + /// <returns>Whether the item has changed.</returns> + public static bool HasChanged<T>(this T source, DateTime asOf) + where T : BaseItem + { + ArgumentNullException.ThrowIfNull(source); + return source.DateModified.Subtract(asOf).Duration().TotalSeconds > 1; + } } } diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index b7b5dac03..ca79e6245 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -11,8 +11,8 @@ using System.Text.Json; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Database.Implementations.Entities; using Jellyfin.Extensions.Json; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index af6348e46..06cbcc2e1 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -11,10 +11,11 @@ using System.Security; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; -using System.Threading.Tasks.Dataflow; using J2N.Collections.Generic.Extensions; -using Jellyfin.Data.Entities; +using Jellyfin.Data; using Jellyfin.Data.Enums; +using Jellyfin.Database.Implementations.Entities; +using Jellyfin.Database.Implementations.Enums; using Jellyfin.Extensions; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Collections; @@ -23,6 +24,7 @@ using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.LibraryTaskScheduler; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Dto; using MediaBrowser.Model.IO; @@ -47,6 +49,8 @@ namespace MediaBrowser.Controller.Entities public static IUserViewManager UserViewManager { get; set; } + public static ILimitedConcurrencyLibraryScheduler LimitedConcurrencyLibraryScheduler { get; set; } + /// <summary> /// Gets or sets a value indicating whether this instance is root. /// </summary> @@ -596,51 +600,13 @@ namespace MediaBrowser.Controller.Entities /// <returns>Task.</returns> private async Task RunTasks<T>(Func<T, IProgress<double>, Task> task, IList<T> children, IProgress<double> progress, CancellationToken cancellationToken) { - var childrenCount = children.Count; - var childrenProgress = new double[childrenCount]; - - void UpdateProgress() - { - progress.Report(childrenProgress.Average()); - } - - var fanoutConcurrency = ConfigurationManager.Configuration.LibraryScanFanoutConcurrency; - var parallelism = fanoutConcurrency > 0 ? fanoutConcurrency : Environment.ProcessorCount; - - var actionBlock = new ActionBlock<int>( - async i => - { - var innerProgress = new Progress<double>(innerPercent => - { - // round the percent and only update progress if it changed to prevent excessive UpdateProgress calls - var innerPercentRounded = Math.Round(innerPercent); - if (childrenProgress[i] != innerPercentRounded) - { - childrenProgress[i] = innerPercentRounded; - UpdateProgress(); - } - }); - - await task(children[i], innerProgress).ConfigureAwait(false); - - childrenProgress[i] = 100; - - UpdateProgress(); - }, - new ExecutionDataflowBlockOptions - { - MaxDegreeOfParallelism = parallelism, - CancellationToken = cancellationToken, - }); - - for (var i = 0; i < childrenCount; i++) - { - await actionBlock.SendAsync(i, cancellationToken).ConfigureAwait(false); - } - - actionBlock.Complete(); - - await actionBlock.Completion.ConfigureAwait(false); + await LimitedConcurrencyLibraryScheduler + .Enqueue( + children.ToArray(), + task, + progress, + cancellationToken) + .ConfigureAwait(false); } /// <summary> @@ -729,7 +695,7 @@ namespace MediaBrowser.Controller.Entities items = GetRecursiveChildren(user, query); } - return PostFilterAndSort(items, query, true); + return PostFilterAndSort(items, query); } if (this is not UserRootFolder @@ -993,10 +959,10 @@ namespace MediaBrowser.Controller.Entities items = GetChildren(user, true, childQuery).Where(filter); } - return PostFilterAndSort(items, query, true); + return PostFilterAndSort(items, query); } - protected QueryResult<BaseItem> PostFilterAndSort(IEnumerable<BaseItem> items, InternalItemsQuery query, bool enableSorting) + protected QueryResult<BaseItem> PostFilterAndSort(IEnumerable<BaseItem> items, InternalItemsQuery query) { var user = query.User; @@ -1006,7 +972,7 @@ namespace MediaBrowser.Controller.Entities items = CollapseBoxSetItemsIfNeeded(items, query, this, user, ConfigurationManager, CollectionManager); } - #pragma warning disable CA1309 +#pragma warning disable CA1309 if (!string.IsNullOrEmpty(query.NameStartsWithOrGreater)) { items = items.Where(i => string.Compare(query.NameStartsWithOrGreater, i.SortName, StringComparison.InvariantCultureIgnoreCase) < 1); @@ -1021,7 +987,7 @@ namespace MediaBrowser.Controller.Entities { items = items.Where(i => string.Compare(query.NameLessThan, i.SortName, StringComparison.InvariantCultureIgnoreCase) == 1); } - #pragma warning restore CA1309 +#pragma warning restore CA1309 // This must be the last filter if (!query.AdjacentTo.IsNullOrEmpty()) @@ -1029,7 +995,7 @@ namespace MediaBrowser.Controller.Entities items = UserViewBuilder.FilterForAdjacency(items.ToList(), query.AdjacentTo.Value); } - return UserViewBuilder.SortAndPage(items, null, query, LibraryManager, enableSorting); + return UserViewBuilder.SortAndPage(items, null, query, LibraryManager); } private static IEnumerable<BaseItem> CollapseBoxSetItemsIfNeeded( @@ -1062,11 +1028,6 @@ namespace MediaBrowser.Controller.Entities return false; } - if (queryParent is Series) - { - return false; - } - if (queryParent is Season) { return false; @@ -1086,12 +1047,15 @@ namespace MediaBrowser.Controller.Entities if (!param.HasValue) { - if (user is not null && !configurationManager.Configuration.EnableGroupingIntoCollections) + if (user is not null && query.IncludeItemTypes.Any(type => + (type == BaseItemKind.Movie && !configurationManager.Configuration.EnableGroupingMoviesIntoCollections) || + (type == BaseItemKind.Series && !configurationManager.Configuration.EnableGroupingShowsIntoCollections))) { return false; } - if (query.IncludeItemTypes.Length == 0 || query.IncludeItemTypes.Contains(BaseItemKind.Movie)) + if (query.IncludeItemTypes.Length == 0 + || query.IncludeItemTypes.Any(type => type == BaseItemKind.Movie || type == BaseItemKind.Series)) { param = true; } @@ -1222,11 +1186,6 @@ namespace MediaBrowser.Controller.Entities return false; } - if (request.IsPlayed.HasValue) - { - return false; - } - if (!string.IsNullOrWhiteSpace(request.Person)) { return false; @@ -1267,17 +1226,15 @@ namespace MediaBrowser.Controller.Entities return false; } - if (request.MinCommunityRating.HasValue) - { - return false; - } - - if (request.MinCriticRating.HasValue) + if (request.MinIndexNumber.HasValue) { return false; } - if (request.MinIndexNumber.HasValue) + if (request.OrderBy.Any(o => + o.OrderBy == ItemSortBy.CommunityRating || + o.OrderBy == ItemSortBy.CriticRating || + o.OrderBy == ItemSortBy.Runtime)) { return false; } diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index 43f02fb72..b32b64f5d 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -3,8 +3,11 @@ using System; using System.Collections.Generic; using System.Linq; -using Jellyfin.Data.Entities; +using Diacritics.Extensions; +using Jellyfin.Data; using Jellyfin.Data.Enums; +using Jellyfin.Database.Implementations.Entities; +using Jellyfin.Database.Implementations.Enums; using MediaBrowser.Controller.Dto; using MediaBrowser.Model.Entities; @@ -230,9 +233,9 @@ namespace MediaBrowser.Controller.Entities public int? IndexNumber { get; set; } - public int? MinParentalRating { get; set; } + public ParentalRatingScore? MinParentalRating { get; set; } - public int? MaxParentalRating { get; set; } + public ParentalRatingScore? MaxParentalRating { get; set; } public bool? HasDeadParentId { get; set; } @@ -304,6 +307,8 @@ namespace MediaBrowser.Controller.Entities public bool? IsDeadStudio { get; set; } + public bool? IsDeadGenre { get; set; } + public bool? IsDeadPerson { get; set; } /// <summary> @@ -358,18 +363,26 @@ namespace MediaBrowser.Controller.Entities public void SetUser(User user) { - MaxParentalRating = user.MaxParentalAgeRating; - - if (MaxParentalRating.HasValue) + var maxRating = user.MaxParentalRatingScore; + if (maxRating.HasValue) { - string other = UnratedItem.Other.ToString(); - BlockUnratedItems = user.GetPreference(PreferenceKind.BlockUnratedItems) - .Where(i => i != other) - .Select(e => Enum.Parse<UnratedItem>(e, true)).ToArray(); + MaxParentalRating = new(maxRating.Value, user.MaxParentalRatingSubScore); } - ExcludeInheritedTags = user.GetPreference(PreferenceKind.BlockedTags); - IncludeInheritedTags = user.GetPreference(PreferenceKind.AllowedTags); + var other = UnratedItem.Other.ToString(); + BlockUnratedItems = user.GetPreference(PreferenceKind.BlockUnratedItems) + .Where(i => i != other) + .Select(e => Enum.Parse<UnratedItem>(e, true)).ToArray(); + + ExcludeInheritedTags = user.GetPreference(PreferenceKind.BlockedTags) + .Where(tag => !string.IsNullOrWhiteSpace(tag)) + .Select(tag => tag.RemoveDiacritics().ToLowerInvariant()) + .ToArray(); + + IncludeInheritedTags = user.GetPreference(PreferenceKind.AllowedTags) + .Where(tag => !string.IsNullOrWhiteSpace(tag)) + .Select(tag => tag.RemoveDiacritics().ToLowerInvariant()) + .ToArray(); User = user; } diff --git a/MediaBrowser.Controller/Entities/InternalPeopleQuery.cs b/MediaBrowser.Controller/Entities/InternalPeopleQuery.cs index 3e1d89274..203a16a66 100644 --- a/MediaBrowser.Controller/Entities/InternalPeopleQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalPeopleQuery.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; -using Jellyfin.Data.Entities; +using Jellyfin.Database.Implementations.Entities; namespace MediaBrowser.Controller.Entities { diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs index c9a93d0f5..dd5852823 100644 --- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs +++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs @@ -7,8 +7,10 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Text.Json.Serialization; -using Jellyfin.Data.Entities; +using Jellyfin.Data; using Jellyfin.Data.Enums; +using Jellyfin.Database.Implementations.Entities; +using Jellyfin.Database.Implementations.Enums; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Querying; @@ -195,7 +197,7 @@ namespace MediaBrowser.Controller.Entities.Movies var expandedFolders = new List<Guid>(); return FlattenItems(this, expandedFolders) - .SelectMany(i => LibraryManager.GetCollectionFolders(i)) + .SelectMany(LibraryManager.GetCollectionFolders) .Select(i => i.Id) .Distinct() .ToArray(); diff --git a/MediaBrowser.Controller/Entities/PeopleHelper.cs b/MediaBrowser.Controller/Entities/PeopleHelper.cs index 4141b1712..24b1843ce 100644 --- a/MediaBrowser.Controller/Entities/PeopleHelper.cs +++ b/MediaBrowser.Controller/Entities/PeopleHelper.cs @@ -15,6 +15,8 @@ namespace MediaBrowser.Controller.Entities ArgumentNullException.ThrowIfNull(person); ArgumentException.ThrowIfNullOrEmpty(person.Name); + person.Name = person.Name.Trim(); + // Normalize if (string.Equals(person.Role, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase)) { diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index 46bad3f3b..6bdba36f9 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -7,12 +7,14 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text.Json.Serialization; +using System.Threading; using Jellyfin.Data.Enums; using Jellyfin.Extensions; +using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; -using MediaBrowser.Model.Providers; using Microsoft.Extensions.Logging; namespace MediaBrowser.Controller.Entities.TV @@ -22,6 +24,8 @@ namespace MediaBrowser.Controller.Entities.TV /// </summary> public class Episode : Video, IHasTrailers, IHasLookupInfo<EpisodeInfo>, IHasSeries { + public static IMediaEncoder MediaEncoder { get; set; } + /// <inheritdoc /> [JsonIgnore] public IReadOnlyList<BaseItem> LocalTrailers => GetExtras() @@ -325,6 +329,39 @@ namespace MediaBrowser.Controller.Entities.TV { if (SourceType == SourceType.Library || SourceType == SourceType.LiveTV) { + var libraryOptions = LibraryManager.GetLibraryOptions(this); + if (libraryOptions.EnableEmbeddedEpisodeInfos && string.Equals(Container, "mp4", StringComparison.OrdinalIgnoreCase)) + { + try + { + var mediaInfo = MediaEncoder.GetMediaInfo( + new MediaInfoRequest + { + MediaSource = GetMediaSources(false)[0], + MediaType = DlnaProfileType.Video + }, + CancellationToken.None).GetAwaiter().GetResult(); + if (mediaInfo.ParentIndexNumber > 0) + { + ParentIndexNumber = mediaInfo.ParentIndexNumber; + } + + if (mediaInfo.IndexNumber > 0) + { + IndexNumber = mediaInfo.IndexNumber; + } + + if (!string.IsNullOrEmpty(mediaInfo.ShowName)) + { + SeriesName = mediaInfo.ShowName; + } + } + catch (Exception ex) + { + Logger.LogError(ex, "Error reading the episode information with ffprobe. Episode: {EpisodeInfo}", Path); + } + } + try { if (LibraryManager.FillMissingEpisodeNumbersFromPath(this, replaceAllMetadata)) diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index e3fbe8e4d..48211d99f 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -7,8 +7,9 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text.Json.Serialization; -using Jellyfin.Data.Entities; +using System.Threading; using Jellyfin.Data.Enums; +using Jellyfin.Database.Implementations.Entities; using Jellyfin.Extensions; using MediaBrowser.Common; using MediaBrowser.Controller.Dto; @@ -152,6 +153,21 @@ namespace MediaBrowser.Controller.Entities.TV protected override QueryResult<BaseItem> GetItemsInternal(InternalItemsQuery query) { + if (SourceType == SourceType.Channel) + { + try + { + query.Parent = this; + query.ChannelIds = new[] { ChannelId }; + return ChannelManager.GetChannelItemsInternal(query, new Progress<double>(), CancellationToken.None).GetAwaiter().GetResult(); + } + catch + { + // Already logged at lower levels + return new QueryResult<BaseItem>(); + } + } + if (query.User is null) { return base.GetItemsInternal(query); @@ -163,7 +179,7 @@ namespace MediaBrowser.Controller.Entities.TV var items = GetEpisodes(user, query.DtoOptions, true).Where(filter); - return PostFilterAndSort(items, query, false); + return PostFilterAndSort(items, query); } /// <summary> @@ -257,7 +273,7 @@ namespace MediaBrowser.Controller.Entities.TV if (!IndexNumber.HasValue && !string.IsNullOrEmpty(Path)) { - IndexNumber ??= LibraryManager.GetSeasonNumberFromPath(Path); + IndexNumber ??= LibraryManager.GetSeasonNumberFromPath(Path, ParentId); // If a change was made record it if (IndexNumber.HasValue) diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 137d91f1c..62c73d56f 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -9,12 +9,13 @@ using System.Linq; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Data.Entities; +using Jellyfin.Data; using Jellyfin.Data.Enums; +using Jellyfin.Database.Implementations.Entities; +using Jellyfin.Database.Implementations.Enums; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Providers; using MediaBrowser.Model.Querying; using MetadataProvider = MediaBrowser.Model.Entities.MetadataProvider; @@ -23,7 +24,7 @@ namespace MediaBrowser.Controller.Entities.TV /// <summary> /// Class Series. /// </summary> - public class Series : Folder, IHasTrailers, IHasDisplayOrder, IHasLookupInfo<SeriesInfo>, IMetadataContainer + public class Series : Folder, IHasTrailers, IHasDisplayOrder, IHasLookupInfo<SeriesInfo>, IMetadataContainer, ISupportsBoxSetGrouping { public Series() { @@ -213,7 +214,7 @@ namespace MediaBrowser.Controller.Entities.TV query.AncestorWithPresentationUniqueKey = null; query.SeriesPresentationUniqueKey = seriesKey; query.IncludeItemTypes = new[] { BaseItemKind.Season }; - query.OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) }; + query.OrderBy = new[] { (ItemSortBy.IndexNumber, SortOrder.Ascending) }; if (user is not null && !user.DisplayMissingEpisodes) { @@ -225,16 +226,27 @@ namespace MediaBrowser.Controller.Entities.TV { var user = query.User; + if (SourceType == SourceType.Channel) + { + try + { + query.Parent = this; + query.ChannelIds = [ChannelId]; + return ChannelManager.GetChannelItemsInternal(query, new Progress<double>(), CancellationToken.None).GetAwaiter().GetResult(); + } + catch + { + // Already logged at lower levels + return new QueryResult<BaseItem>(); + } + } + if (query.Recursive) { var seriesKey = GetUniqueSeriesKey(this); query.AncestorWithPresentationUniqueKey = null; query.SeriesPresentationUniqueKey = seriesKey; - if (query.OrderBy.Count == 0) - { - query.OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) }; - } if (query.IncludeItemTypes.Length == 0) { @@ -371,7 +383,25 @@ namespace MediaBrowser.Controller.Entities.TV query.IsMissing = false; } - var allItems = LibraryManager.GetItemList(query); + IReadOnlyList<BaseItem> allItems; + if (SourceType == SourceType.Channel) + { + try + { + query.Parent = parentSeason; + query.ChannelIds = [ChannelId]; + allItems = [.. ChannelManager.GetChannelItemsInternal(query, new Progress<double>(), CancellationToken.None).GetAwaiter().GetResult().Items]; + } + catch + { + // Already logged at lower levels + return []; + } + } + else + { + allItems = LibraryManager.GetItemList(query); + } return GetSeasonEpisodes(parentSeason, user, allItems, options, shouldIncludeMissingEpisodes); } diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs index 7ae4a4a2c..deed3631b 100644 --- a/MediaBrowser.Controller/Entities/UserRootFolder.cs +++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs @@ -8,7 +8,7 @@ using System.Linq; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Data.Entities; +using Jellyfin.Database.Implementations.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Library; using MediaBrowser.Model.Querying; @@ -80,7 +80,7 @@ namespace MediaBrowser.Controller.Entities PresetViews = query.PresetViews }); - return UserViewBuilder.SortAndPage(result, null, query, LibraryManager, true); + return UserViewBuilder.SortAndPage(result, null, query, LibraryManager); } public override int GetChildCount(User user) diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs index f5ca3737c..dfa31315c 100644 --- a/MediaBrowser.Controller/Entities/UserView.cs +++ b/MediaBrowser.Controller/Entities/UserView.cs @@ -8,8 +8,8 @@ using System.Linq; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Database.Implementations.Entities; using Jellyfin.Extensions; using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.TV; diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index 4ec2e4c0a..7679d383f 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -6,8 +6,10 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; -using Jellyfin.Data.Entities; +using Jellyfin.Data; using Jellyfin.Data.Enums; +using Jellyfin.Database.Implementations.Entities; +using Jellyfin.Database.Implementations.Enums; using Jellyfin.Extensions; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.TV; @@ -436,22 +438,18 @@ namespace MediaBrowser.Controller.Entities items = FilterForAdjacency(items.ToList(), query.AdjacentTo.Value); } - return SortAndPage(items, totalRecordLimit, query, libraryManager, true); + return SortAndPage(items, totalRecordLimit, query, libraryManager); } public static QueryResult<BaseItem> SortAndPage( IEnumerable<BaseItem> items, int? totalRecordLimit, InternalItemsQuery query, - ILibraryManager libraryManager, - bool enableSorting) + ILibraryManager libraryManager) { - if (enableSorting) + if (query.OrderBy.Count > 0) { - if (query.OrderBy.Count > 0) - { - items = libraryManager.Sort(items, query.User, query.OrderBy); - } + items = libraryManager.Sort(items, query.User, query.OrderBy); } var itemsArray = totalRecordLimit.HasValue ? items.Take(totalRecordLimit.Value).ToArray() : items.ToArray(); @@ -922,6 +920,11 @@ namespace MediaBrowser.Controller.Entities } } + if (query.ExcludeItemIds.Contains(item.Id)) + { + return false; + } + return true; } |
