瀏覽代碼

WIP porting new Repository structure

JPVenson 7 月之前
父節點
當前提交
b09a41ad1f
共有 50 個文件被更改,包括 211 次插入162 次删除
  1. 3 0
      .editorconfig
  2. 8 4
      Emby.Server.Implementations/Dto/DtoService.cs
  3. 19 16
      Emby.Server.Implementations/Library/LibraryManager.cs
  4. 15 10
      Emby.Server.Implementations/Library/MediaSourceManager.cs
  5. 10 9
      Emby.Server.Implementations/Library/MusicManager.cs
  6. 1 1
      Emby.Server.Implementations/Library/SearchEngine.cs
  7. 7 2
      Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
  8. 1 1
      Jellyfin.Api/Controllers/LibraryController.cs
  9. 2 2
      Jellyfin.Api/Controllers/MoviesController.cs
  10. 4 3
      Jellyfin.Api/Controllers/YearsController.cs
  11. 1 1
      Jellyfin.Server.Implementations/Item/MediaStreamRepository.cs
  12. 3 1
      Jellyfin.Server.Implementations/Item/PeopleRepository.cs
  13. 0 1
      Jellyfin.Server.Implementations/ModelConfiguration/ChapterConfiguration.cs
  14. 1 1
      Jellyfin.Server.Implementations/Trickplay/TrickplayManager.cs
  15. 1 1
      MediaBrowser.Controller/Entities/AggregateFolder.cs
  16. 1 1
      MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
  17. 1 1
      MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
  18. 5 4
      MediaBrowser.Controller/Entities/BaseItem.cs
  19. 17 14
      MediaBrowser.Controller/Entities/Folder.cs
  20. 1 1
      MediaBrowser.Controller/Entities/Genre.cs
  21. 2 2
      MediaBrowser.Controller/Entities/IHasMediaSources.cs
  22. 1 1
      MediaBrowser.Controller/Entities/IItemByName.cs
  23. 7 6
      MediaBrowser.Controller/Entities/Movies/BoxSet.cs
  24. 1 1
      MediaBrowser.Controller/Entities/PeopleHelper.cs
  25. 1 1
      MediaBrowser.Controller/Entities/Person.cs
  26. 1 1
      MediaBrowser.Controller/Entities/Studio.cs
  27. 2 2
      MediaBrowser.Controller/Entities/TV/Series.cs
  28. 1 1
      MediaBrowser.Controller/Entities/UserRootFolder.cs
  29. 2 2
      MediaBrowser.Controller/Entities/UserView.cs
  30. 1 1
      MediaBrowser.Controller/Entities/UserViewBuilder.cs
  31. 1 1
      MediaBrowser.Controller/Entities/Year.cs
  32. 9 9
      MediaBrowser.Controller/Library/ILibraryManager.cs
  33. 4 4
      MediaBrowser.Controller/Library/IMediaSourceManager.cs
  34. 3 3
      MediaBrowser.Controller/Library/IMusicManager.cs
  35. 5 4
      MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
  36. 5 5
      MediaBrowser.Controller/Playlists/Playlist.cs
  37. 11 5
      MediaBrowser.Controller/Providers/MetadataResult.cs
  38. 1 1
      MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs
  39. 10 15
      MediaBrowser.Providers/Manager/MetadataService.cs
  40. 6 2
      MediaBrowser.Providers/MediaInfo/AudioFileProber.cs
  41. 1 1
      MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
  42. 11 4
      MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
  43. 10 3
      MediaBrowser.Providers/MediaInfo/ProbeProvider.cs
  44. 3 3
      MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs
  45. 2 2
      MediaBrowser.Providers/Music/AlbumMetadataService.cs
  46. 3 2
      MediaBrowser.Providers/Music/ArtistMetadataService.cs
  47. 1 1
      MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
  48. 3 3
      MediaBrowser.Providers/TV/SeasonMetadataService.cs
  49. 1 1
      MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs
  50. 1 1
      MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs

+ 3 - 0
.editorconfig

@@ -192,3 +192,6 @@ csharp_space_between_method_call_empty_parameter_list_parentheses = false
 # Wrapping preferences
 csharp_preserve_single_line_statements = true
 csharp_preserve_single_line_blocks = true
+
+# CA1826: Do not use Enumerable methods on indexable collections
+dotnet_diagnostic.CA1826.severity = suggestion

+ 8 - 4
Emby.Server.Implementations/Dto/DtoService.cs

@@ -10,6 +10,7 @@ using Jellyfin.Data.Enums;
 using Jellyfin.Extensions;
 using MediaBrowser.Common;
 using MediaBrowser.Controller.Channels;
+using MediaBrowser.Controller.Chapters;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
@@ -51,6 +52,7 @@ namespace Emby.Server.Implementations.Dto
         private readonly Lazy<ILiveTvManager> _livetvManagerFactory;
 
         private readonly ITrickplayManager _trickplayManager;
+        private readonly IChapterRepository _chapterRepository;
 
         public DtoService(
             ILogger<DtoService> logger,
@@ -63,7 +65,8 @@ namespace Emby.Server.Implementations.Dto
             IApplicationHost appHost,
             IMediaSourceManager mediaSourceManager,
             Lazy<ILiveTvManager> livetvManagerFactory,
-            ITrickplayManager trickplayManager)
+            ITrickplayManager trickplayManager,
+            IChapterRepository chapterRepository)
         {
             _logger = logger;
             _libraryManager = libraryManager;
@@ -76,6 +79,7 @@ namespace Emby.Server.Implementations.Dto
             _mediaSourceManager = mediaSourceManager;
             _livetvManagerFactory = livetvManagerFactory;
             _trickplayManager = trickplayManager;
+            _chapterRepository = chapterRepository;
         }
 
         private ILiveTvManager LivetvManager => _livetvManagerFactory.Value;
@@ -165,7 +169,7 @@ namespace Emby.Server.Implementations.Dto
             return dto;
         }
 
-        private static IList<BaseItem> GetTaggedItems(IItemByName byName, User? user, DtoOptions options)
+        private static IReadOnlyList<BaseItem> GetTaggedItems(IItemByName byName, User? user, DtoOptions options)
         {
             return byName.GetTaggedItems(
                 new InternalItemsQuery(user)
@@ -327,7 +331,7 @@ namespace Emby.Server.Implementations.Dto
             return dto;
         }
 
-        private static void SetItemByNameInfo(BaseItem item, BaseItemDto dto, IList<BaseItem> taggedItems)
+        private static void SetItemByNameInfo(BaseItem item, BaseItemDto dto, IReadOnlyList<BaseItem> taggedItems)
         {
             if (item is MusicArtist)
             {
@@ -1060,7 +1064,7 @@ namespace Emby.Server.Implementations.Dto
 
                 if (options.ContainsField(ItemFields.Chapters))
                 {
-                    dto.Chapters = _itemRepo.GetChapters(item);
+                    dto.Chapters = _chapterRepository.GetChapters(item.Id).ToList();
                 }
 
                 if (options.ContainsField(ItemFields.Trickplay))

+ 19 - 16
Emby.Server.Implementations/Library/LibraryManager.cs

@@ -76,6 +76,7 @@ namespace Emby.Server.Implementations.Library
         private readonly IItemRepository _itemRepository;
         private readonly IImageProcessor _imageProcessor;
         private readonly NamingOptions _namingOptions;
+        private readonly IPeopleRepository _peopleRepository;
         private readonly ExtraResolver _extraResolver;
 
         /// <summary>
@@ -112,6 +113,7 @@ namespace Emby.Server.Implementations.Library
         /// <param name="imageProcessor">The image processor.</param>
         /// <param name="namingOptions">The naming options.</param>
         /// <param name="directoryService">The directory service.</param>
+        /// <param name="peopleRepository">The People Repository.</param>
         public LibraryManager(
             IServerApplicationHost appHost,
             ILoggerFactory loggerFactory,
@@ -127,7 +129,8 @@ namespace Emby.Server.Implementations.Library
             IItemRepository itemRepository,
             IImageProcessor imageProcessor,
             NamingOptions namingOptions,
-            IDirectoryService directoryService)
+            IDirectoryService directoryService,
+            IPeopleRepository peopleRepository)
         {
             _appHost = appHost;
             _logger = loggerFactory.CreateLogger<LibraryManager>();
@@ -144,7 +147,7 @@ namespace Emby.Server.Implementations.Library
             _imageProcessor = imageProcessor;
             _cache = new ConcurrentDictionary<Guid, BaseItem>();
             _namingOptions = namingOptions;
-
+            _peopleRepository = peopleRepository;
             _extraResolver = new ExtraResolver(loggerFactory.CreateLogger<ExtraResolver>(), namingOptions, directoryService);
 
             _configurationManager.ConfigurationUpdated += ConfigurationUpdated;
@@ -1274,7 +1277,7 @@ namespace Emby.Server.Implementations.Library
             return ItemIsVisible(item, user) ? item : null;
         }
 
-        public List<BaseItem> GetItemList(InternalItemsQuery query, bool allowExternalContent)
+        public IReadOnlyList<BaseItem> GetItemList(InternalItemsQuery query, bool allowExternalContent)
         {
             if (query.Recursive && !query.ParentId.IsEmpty())
             {
@@ -1300,7 +1303,7 @@ namespace Emby.Server.Implementations.Library
             return itemList;
         }
 
-        public List<BaseItem> GetItemList(InternalItemsQuery query)
+        public IReadOnlyList<BaseItem> GetItemList(InternalItemsQuery query)
         {
             return GetItemList(query, true);
         }
@@ -1324,7 +1327,7 @@ namespace Emby.Server.Implementations.Library
             return _itemRepository.GetCount(query);
         }
 
-        public List<BaseItem> GetItemList(InternalItemsQuery query, List<BaseItem> parents)
+        public IReadOnlyList<BaseItem> GetItemList(InternalItemsQuery query, List<BaseItem> parents)
         {
             SetTopParentIdsOrAncestors(query, parents);
 
@@ -1357,7 +1360,7 @@ namespace Emby.Server.Implementations.Library
                 _itemRepository.GetItemList(query));
         }
 
-        public List<Guid> GetItemIds(InternalItemsQuery query)
+        public IReadOnlyList<Guid> GetItemIds(InternalItemsQuery query)
         {
             if (query.User is not null)
             {
@@ -2736,12 +2739,12 @@ namespace Emby.Server.Implementations.Library
             return path;
         }
 
-        public List<PersonInfo> GetPeople(InternalPeopleQuery query)
+        public IReadOnlyList<PersonInfo> GetPeople(InternalPeopleQuery query)
         {
-            return _itemRepository.GetPeople(query);
+            return _peopleRepository.GetPeople(query);
         }
 
-        public List<PersonInfo> GetPeople(BaseItem item)
+        public IReadOnlyList<PersonInfo> GetPeople(BaseItem item)
         {
             if (item.SupportsPeople)
             {
@@ -2756,12 +2759,12 @@ namespace Emby.Server.Implementations.Library
                 }
             }
 
-            return new List<PersonInfo>();
+            return [];
         }
 
-        public List<Person> GetPeopleItems(InternalPeopleQuery query)
+        public IReadOnlyList<Person> GetPeopleItems(InternalPeopleQuery query)
         {
-            return _itemRepository.GetPeopleNames(query)
+            return _peopleRepository.GetPeopleNames(query)
             .Select(i =>
             {
                 try
@@ -2779,9 +2782,9 @@ namespace Emby.Server.Implementations.Library
             .ToList()!; // null values are filtered out
         }
 
-        public List<string> GetPeopleNames(InternalPeopleQuery query)
+        public IReadOnlyList<string> GetPeopleNames(InternalPeopleQuery query)
         {
-            return _itemRepository.GetPeopleNames(query);
+            return _peopleRepository.GetPeopleNames(query);
         }
 
         public void UpdatePeople(BaseItem item, List<PersonInfo> people)
@@ -2790,14 +2793,14 @@ namespace Emby.Server.Implementations.Library
         }
 
         /// <inheritdoc />
-        public async Task UpdatePeopleAsync(BaseItem item, List<PersonInfo> people, CancellationToken cancellationToken)
+        public async Task UpdatePeopleAsync(BaseItem item, IReadOnlyList<PersonInfo> people, CancellationToken cancellationToken)
         {
             if (!item.SupportsPeople)
             {
                 return;
             }
 
-            _itemRepository.UpdatePeople(item.Id, people);
+            _peopleRepository.UpdatePeople(item.Id, people);
             if (people is not null)
             {
                 await SavePeopleMetadataAsync(people, cancellationToken).ConfigureAwait(false);

+ 15 - 10
Emby.Server.Implementations/Library/MediaSourceManager.cs

@@ -51,7 +51,8 @@ namespace Emby.Server.Implementations.Library
         private readonly ILocalizationManager _localizationManager;
         private readonly IApplicationPaths _appPaths;
         private readonly IDirectoryService _directoryService;
-
+        private readonly IMediaStreamRepository _mediaStreamRepository;
+        private readonly IMediaAttachmentRepository _mediaAttachmentRepository;
         private readonly ConcurrentDictionary<string, ILiveStream> _openStreams = new ConcurrentDictionary<string, ILiveStream>(StringComparer.OrdinalIgnoreCase);
         private readonly AsyncNonKeyedLocker _liveStreamLocker = new(1);
         private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
@@ -69,7 +70,9 @@ namespace Emby.Server.Implementations.Library
             IFileSystem fileSystem,
             IUserDataManager userDataManager,
             IMediaEncoder mediaEncoder,
-            IDirectoryService directoryService)
+            IDirectoryService directoryService,
+            IMediaStreamRepository mediaStreamRepository,
+            IMediaAttachmentRepository mediaAttachmentRepository)
         {
             _appHost = appHost;
             _itemRepo = itemRepo;
@@ -82,6 +85,8 @@ namespace Emby.Server.Implementations.Library
             _localizationManager = localizationManager;
             _appPaths = applicationPaths;
             _directoryService = directoryService;
+            _mediaStreamRepository = mediaStreamRepository;
+            _mediaAttachmentRepository = mediaAttachmentRepository;
         }
 
         public void AddParts(IEnumerable<IMediaSourceProvider> providers)
@@ -89,9 +94,9 @@ namespace Emby.Server.Implementations.Library
             _providers = providers.ToArray();
         }
 
-        public List<MediaStream> GetMediaStreams(MediaStreamQuery query)
+        public IReadOnlyList<MediaStream> GetMediaStreams(MediaStreamQuery query)
         {
-            var list = _itemRepo.GetMediaStreams(query);
+            var list = _mediaStreamRepository.GetMediaStreams(query);
 
             foreach (var stream in list)
             {
@@ -121,7 +126,7 @@ namespace Emby.Server.Implementations.Library
             return false;
         }
 
-        public List<MediaStream> GetMediaStreams(Guid itemId)
+        public IReadOnlyList<MediaStream> GetMediaStreams(Guid itemId)
         {
             var list = GetMediaStreams(new MediaStreamQuery
             {
@@ -131,7 +136,7 @@ namespace Emby.Server.Implementations.Library
             return GetMediaStreamsForItem(list);
         }
 
-        private List<MediaStream> GetMediaStreamsForItem(List<MediaStream> streams)
+        private IReadOnlyList<MediaStream> GetMediaStreamsForItem(IReadOnlyList<MediaStream> streams)
         {
             foreach (var stream in streams)
             {
@@ -145,13 +150,13 @@ namespace Emby.Server.Implementations.Library
         }
 
         /// <inheritdoc />
-        public List<MediaAttachment> GetMediaAttachments(MediaAttachmentQuery query)
+        public IReadOnlyList<MediaAttachment> GetMediaAttachments(MediaAttachmentQuery query)
         {
-            return _itemRepo.GetMediaAttachments(query);
+            return _mediaAttachmentRepository.GetMediaAttachments(query);
         }
 
         /// <inheritdoc />
-        public List<MediaAttachment> GetMediaAttachments(Guid itemId)
+        public IReadOnlyList<MediaAttachment> GetMediaAttachments(Guid itemId)
         {
             return GetMediaAttachments(new MediaAttachmentQuery
             {
@@ -332,7 +337,7 @@ namespace Emby.Server.Implementations.Library
             return sources.FirstOrDefault(i => string.Equals(i.Id, mediaSourceId, StringComparison.OrdinalIgnoreCase));
         }
 
-        public List<MediaSourceInfo> GetStaticMediaSources(BaseItem item, bool enablePathSubstitution, User user = null)
+        public IReadOnlyList<MediaSourceInfo> GetStaticMediaSources(BaseItem item, bool enablePathSubstitution, User user = null)
         {
             ArgumentNullException.ThrowIfNull(item);
 

+ 10 - 9
Emby.Server.Implementations/Library/MusicManager.cs

@@ -2,6 +2,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Collections.Immutable;
 using System.Linq;
 using Jellyfin.Data.Entities;
 using Jellyfin.Data.Enums;
@@ -24,7 +25,7 @@ namespace Emby.Server.Implementations.Library
             _libraryManager = libraryManager;
         }
 
-        public List<BaseItem> GetInstantMixFromSong(Audio item, User? user, DtoOptions dtoOptions)
+        public IReadOnlyList<BaseItem> GetInstantMixFromSong(Audio item, User? user, DtoOptions dtoOptions)
         {
             var list = new List<BaseItem>
             {
@@ -33,21 +34,21 @@ namespace Emby.Server.Implementations.Library
 
             list.AddRange(GetInstantMixFromGenres(item.Genres, user, dtoOptions));
 
-            return list;
+            return list.ToImmutableList();
         }
 
         /// <inheritdoc />
-        public List<BaseItem> GetInstantMixFromArtist(MusicArtist artist, User? user, DtoOptions dtoOptions)
+        public IReadOnlyList<BaseItem> GetInstantMixFromArtist(MusicArtist artist, User? user, DtoOptions dtoOptions)
         {
             return GetInstantMixFromGenres(artist.Genres, user, dtoOptions);
         }
 
-        public List<BaseItem> GetInstantMixFromAlbum(MusicAlbum item, User? user, DtoOptions dtoOptions)
+        public IReadOnlyList<BaseItem> GetInstantMixFromAlbum(MusicAlbum item, User? user, DtoOptions dtoOptions)
         {
             return GetInstantMixFromGenres(item.Genres, user, dtoOptions);
         }
 
-        public List<BaseItem> GetInstantMixFromFolder(Folder item, User? user, DtoOptions dtoOptions)
+        public IReadOnlyList<BaseItem> GetInstantMixFromFolder(Folder item, User? user, DtoOptions dtoOptions)
         {
             var genres = item
                .GetRecursiveChildren(user, new InternalItemsQuery(user)
@@ -63,12 +64,12 @@ namespace Emby.Server.Implementations.Library
             return GetInstantMixFromGenres(genres, user, dtoOptions);
         }
 
-        public List<BaseItem> GetInstantMixFromPlaylist(Playlist item, User? user, DtoOptions dtoOptions)
+        public IReadOnlyList<BaseItem> GetInstantMixFromPlaylist(Playlist item, User? user, DtoOptions dtoOptions)
         {
             return GetInstantMixFromGenres(item.Genres, user, dtoOptions);
         }
 
-        public List<BaseItem> GetInstantMixFromGenres(IEnumerable<string> genres, User? user, DtoOptions dtoOptions)
+        public IReadOnlyList<BaseItem> GetInstantMixFromGenres(IEnumerable<string> genres, User? user, DtoOptions dtoOptions)
         {
             var genreIds = genres.DistinctNames().Select(i =>
             {
@@ -85,7 +86,7 @@ namespace Emby.Server.Implementations.Library
             return GetInstantMixFromGenreIds(genreIds, user, dtoOptions);
         }
 
-        public List<BaseItem> GetInstantMixFromGenreIds(Guid[] genreIds, User? user, DtoOptions dtoOptions)
+        public IReadOnlyList<BaseItem> GetInstantMixFromGenreIds(Guid[] genreIds, User? user, DtoOptions dtoOptions)
         {
             return _libraryManager.GetItemList(new InternalItemsQuery(user)
             {
@@ -97,7 +98,7 @@ namespace Emby.Server.Implementations.Library
             });
         }
 
-        public List<BaseItem> GetInstantMixFromItem(BaseItem item, User? user, DtoOptions dtoOptions)
+        public IReadOnlyList<BaseItem> GetInstantMixFromItem(BaseItem item, User? user, DtoOptions dtoOptions)
         {
             if (item is MusicGenre)
             {

+ 1 - 1
Emby.Server.Implementations/Library/SearchEngine.cs

@@ -171,7 +171,7 @@ namespace Emby.Server.Implementations.Library
                 }
             };
 
-            List<BaseItem> mediaItems;
+            IReadOnlyList<BaseItem> mediaItems;
 
             if (searchQuery.IncludeItemTypes.Length == 1 && searchQuery.IncludeItemTypes[0] == BaseItemKind.MusicArtist)
             {

+ 7 - 2
Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs

@@ -7,6 +7,7 @@ using System.Threading.Tasks;
 using Jellyfin.Data.Enums;
 using Jellyfin.Extensions;
 using MediaBrowser.Common.Configuration;
+using MediaBrowser.Controller.Chapters;
 using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
@@ -32,6 +33,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
         private readonly IEncodingManager _encodingManager;
         private readonly IFileSystem _fileSystem;
         private readonly ILocalizationManager _localization;
+        private readonly IChapterRepository _chapterRepository;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="ChapterImagesTask" /> class.
@@ -43,6 +45,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
         /// <param name="encodingManager">Instance of the <see cref="IEncodingManager"/> interface.</param>
         /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
         /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
+        /// <param name="chapterRepository">Instance of the <see cref="IChapterRepository"/> interface.</param>
         public ChapterImagesTask(
             ILogger<ChapterImagesTask> logger,
             ILibraryManager libraryManager,
@@ -50,7 +53,8 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
             IApplicationPaths appPaths,
             IEncodingManager encodingManager,
             IFileSystem fileSystem,
-            ILocalizationManager localization)
+            ILocalizationManager localization,
+            IChapterRepository chapterRepository)
         {
             _logger = logger;
             _libraryManager = libraryManager;
@@ -59,6 +63,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
             _encodingManager = encodingManager;
             _fileSystem = fileSystem;
             _localization = localization;
+            _chapterRepository = chapterRepository;
         }
 
         /// <inheritdoc />
@@ -141,7 +146,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
 
                 try
                 {
-                    var chapters = _itemRepo.GetChapters(video);
+                    var chapters = _chapterRepository.GetChapters(video.Id);
 
                     var success = await _encodingManager.RefreshChapterImages(video, directoryService, chapters, extract, true, cancellationToken).ConfigureAwait(false);
 

+ 1 - 1
Jellyfin.Api/Controllers/LibraryController.cs

@@ -793,7 +793,7 @@ public class LibraryController : BaseJellyfinApiController
             query.ExcludeArtistIds = excludeArtistIds;
         }
 
-        List<BaseItem> itemsResult = _libraryManager.GetItemList(query);
+        var itemsResult = _libraryManager.GetItemList(query);
 
         var returnList = _dtoService.GetBaseItemDtos(itemsResult, dtoOptions, user);
 

+ 2 - 2
Jellyfin.Api/Controllers/MoviesController.cs

@@ -97,7 +97,7 @@ public class MoviesController : BaseJellyfinApiController
             DtoOptions = dtoOptions
         };
 
-        var recentlyPlayedMovies = _libraryManager.GetItemList(query);
+        var recentlyPlayedMovies = _libraryManager.GetItemList(query)!;
 
         var itemTypes = new List<BaseItemKind> { BaseItemKind.Movie };
         if (_serverConfigurationManager.Configuration.EnableExternalContentInSuggestions)
@@ -120,7 +120,7 @@ public class MoviesController : BaseJellyfinApiController
             DtoOptions = dtoOptions
         });
 
-        var mostRecentMovies = recentlyPlayedMovies.GetRange(0, Math.Min(recentlyPlayedMovies.Count, 6));
+        var mostRecentMovies = recentlyPlayedMovies.Take(Math.Min(recentlyPlayedMovies.Count, 6));
         // Get recently played directors
         var recentDirectors = GetDirectors(mostRecentMovies)
             .ToList();

+ 4 - 3
Jellyfin.Api/Controllers/YearsController.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Collections.Immutable;
 using System.ComponentModel.DataAnnotations;
 using System.Linq;
 using Jellyfin.Api.Extensions;
@@ -105,18 +106,18 @@ public class YearsController : BaseJellyfinApiController
 
         bool Filter(BaseItem i) => FilterItem(i, excludeItemTypes, includeItemTypes, mediaTypes);
 
-        IList<BaseItem> items;
+        IReadOnlyList<BaseItem> items;
         if (parentItem.IsFolder)
         {
             var folder = (Folder)parentItem;
 
             if (userId.IsNullOrEmpty())
             {
-                items = recursive ? folder.GetRecursiveChildren(Filter) : folder.Children.Where(Filter).ToList();
+                items = recursive ? folder.GetRecursiveChildren(Filter) : folder.Children.Where(Filter).ToImmutableList();
             }
             else
             {
-                items = recursive ? folder.GetRecursiveChildren(user, query).ToList() : folder.GetChildren(user, true).Where(Filter).ToList();
+                items = recursive ? folder.GetRecursiveChildren(user, query) : folder.GetChildren(user, true).Where(Filter).ToImmutableList();
             }
         }
         else

+ 1 - 1
Jellyfin.Server.Implementations/Item/MediaStreamRepository.cs

@@ -174,7 +174,7 @@ public class MediaStreamRepository(IDbContextFactory<JellyfinDbContext> dbProvid
             Level = (float)dto.Level.GetValueOrDefault(),
             PixelFormat = dto.PixelFormat,
             BitDepth = dto.BitDepth.GetValueOrDefault(0),
-            IsAnamorphic = dto.IsAnamorphic.GetValueOrDefault(0),
+            IsAnamorphic = dto.IsAnamorphic.GetValueOrDefault(),
             RefFrames = dto.RefFrames.GetValueOrDefault(0),
             CodecTag = dto.CodecTag,
             Comment = dto.Comment,

+ 3 - 1
Jellyfin.Server.Implementations/Item/PeopleRepository.cs

@@ -89,7 +89,9 @@ public class PeopleRepository(IDbContextFactory<JellyfinDbContext> dbProvider) :
             Name = people.Name,
             Role = people.Role,
             SortOrder = people.SortOrder,
-            PersonType = people.Type.ToString()
+            PersonType = people.Type.ToString(),
+            Item = null!,
+            ListOrder = people.SortOrder
         };
 
         return personInfo;

+ 0 - 1
Jellyfin.Server.Implementations/ModelConfiguration/ChapterConfiguration.cs

@@ -5,7 +5,6 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders;
 
 namespace Jellyfin.Server.Implementations.ModelConfiguration;
 
-
 /// <summary>
 /// Chapter configuration.
 /// </summary>

+ 1 - 1
Jellyfin.Server.Implementations/Trickplay/TrickplayManager.cs

@@ -179,7 +179,7 @@ public class TrickplayManager : ITrickplayManager
             {
                 // Extract images
                 // Note: Media sources under parent items exist as their own video/item as well. Only use this video stream for trickplay.
-                var mediaSource = video.GetMediaSources(false).Find(source => Guid.Parse(source.Id).Equals(video.Id));
+                var mediaSource = video.GetMediaSources(false).FirstOrDefault(source => Guid.Parse(source.Id).Equals(video.Id));
 
                 if (mediaSource is null)
                 {

+ 1 - 1
MediaBrowser.Controller/Entities/AggregateFolder.cs

@@ -64,7 +64,7 @@ namespace MediaBrowser.Controller.Entities
             return CreateResolveArgs(directoryService, true).FileSystemChildren;
         }
 
-        protected override List<BaseItem> LoadChildren()
+        protected override IReadOnlyList<BaseItem> LoadChildren()
         {
             lock (_childIdsLock)
             {

+ 1 - 1
MediaBrowser.Controller/Entities/Audio/MusicArtist.cs

@@ -84,7 +84,7 @@ namespace MediaBrowser.Controller.Entities.Audio
             return !IsAccessedByName;
         }
 
-        public IList<BaseItem> GetTaggedItems(InternalItemsQuery query)
+        public IReadOnlyList<BaseItem> GetTaggedItems(InternalItemsQuery query)
         {
             if (query.IncludeItemTypes.Length == 0)
             {

+ 1 - 1
MediaBrowser.Controller/Entities/Audio/MusicGenre.cs

@@ -64,7 +64,7 @@ namespace MediaBrowser.Controller.Entities.Audio
             return true;
         }
 
-        public IList<BaseItem> GetTaggedItems(InternalItemsQuery query)
+        public IReadOnlyList<BaseItem> GetTaggedItems(InternalItemsQuery query)
         {
             query.GenreIds = new[] { Id };
             query.IncludeItemTypes = new[] { BaseItemKind.MusicVideo, BaseItemKind.Audio, BaseItemKind.MusicAlbum, BaseItemKind.MusicArtist };

+ 5 - 4
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -4,6 +4,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Collections.Immutable;
 using System.Globalization;
 using System.IO;
 using System.Linq;
@@ -1044,7 +1045,7 @@ namespace MediaBrowser.Controller.Entities
             return PlayAccess.Full;
         }
 
-        public virtual List<MediaStream> GetMediaStreams()
+        public virtual IReadOnlyList<MediaStream> GetMediaStreams()
         {
             return MediaSourceManager.GetMediaStreams(new MediaStreamQuery
             {
@@ -1057,7 +1058,7 @@ namespace MediaBrowser.Controller.Entities
             return false;
         }
 
-        public virtual List<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
+        public virtual IReadOnlyList<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
         {
             if (SourceType == SourceType.Channel)
             {
@@ -1091,7 +1092,7 @@ namespace MediaBrowser.Controller.Entities
                 return 1;
             }).ThenBy(i => i.Video3DFormat.HasValue ? 1 : 0)
             .ThenByDescending(i => i, new MediaSourceWidthComparator())
-            .ToList();
+            .ToImmutableList();
         }
 
         protected virtual IEnumerable<(BaseItem Item, MediaSourceType MediaSourceType)> GetAllItemsForMediaSources()
@@ -2527,7 +2528,7 @@ namespace MediaBrowser.Controller.Entities
         /// </summary>
         /// <param name="children">Media children.</param>
         /// <returns><c>true</c> if the rating was updated; otherwise <c>false</c>.</returns>
-        public bool UpdateRatingToItems(IList<BaseItem> children)
+        public bool UpdateRatingToItems(IReadOnlyList<BaseItem> children)
         {
             var currentOfficialRating = OfficialRating;
 

+ 17 - 14
MediaBrowser.Controller/Entities/Folder.cs

@@ -4,6 +4,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Collections.Immutable;
 using System.IO;
 using System.Linq;
 using System.Security;
@@ -11,6 +12,7 @@ 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.Enums;
 using Jellyfin.Extensions;
@@ -247,7 +249,7 @@ namespace MediaBrowser.Controller.Entities
         /// We want this synchronous.
         /// </summary>
         /// <returns>Returns children.</returns>
-        protected virtual List<BaseItem> LoadChildren()
+        protected virtual IReadOnlyList<BaseItem> LoadChildren()
         {
             // logger.LogDebug("Loading children from {0} {1} {2}", GetType().Name, Id, Path);
             // just load our children from the repo - the library will be validated and maintained in other processes
@@ -659,7 +661,7 @@ namespace MediaBrowser.Controller.Entities
         /// Get our children from the repo - stubbed for now.
         /// </summary>
         /// <returns>IEnumerable{BaseItem}.</returns>
-        protected List<BaseItem> GetCachedChildren()
+        protected IReadOnlyList<BaseItem> GetCachedChildren()
         {
             return ItemRepository.GetItemList(new InternalItemsQuery
             {
@@ -1283,14 +1285,14 @@ namespace MediaBrowser.Controller.Entities
             return true;
         }
 
-        public List<BaseItem> GetChildren(User user, bool includeLinkedChildren)
+        public IReadOnlyList<BaseItem> GetChildren(User user, bool includeLinkedChildren)
         {
             ArgumentNullException.ThrowIfNull(user);
 
             return GetChildren(user, includeLinkedChildren, new InternalItemsQuery(user));
         }
 
-        public virtual List<BaseItem> GetChildren(User user, bool includeLinkedChildren, InternalItemsQuery query)
+        public virtual IReadOnlyList<BaseItem> GetChildren(User user, bool includeLinkedChildren, InternalItemsQuery query)
         {
             ArgumentNullException.ThrowIfNull(user);
 
@@ -1304,7 +1306,7 @@ namespace MediaBrowser.Controller.Entities
 
             AddChildren(user, includeLinkedChildren, result, false, query);
 
-            return result.Values.ToList();
+            return result.Values.ToImmutableList();
         }
 
         protected virtual IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
@@ -1369,7 +1371,7 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
-        public virtual IEnumerable<BaseItem> GetRecursiveChildren(User user, InternalItemsQuery query)
+        public virtual IReadOnlyList<BaseItem> GetRecursiveChildren(User user, InternalItemsQuery query)
         {
             ArgumentNullException.ThrowIfNull(user);
 
@@ -1377,35 +1379,35 @@ namespace MediaBrowser.Controller.Entities
 
             AddChildren(user, true, result, true, query);
 
-            return result.Values;
+            return result.Values.ToImmutableList();
         }
 
         /// <summary>
         /// Gets the recursive children.
         /// </summary>
         /// <returns>IList{BaseItem}.</returns>
-        public IList<BaseItem> GetRecursiveChildren()
+        public IReadOnlyList<BaseItem> GetRecursiveChildren()
         {
             return GetRecursiveChildren(true);
         }
 
-        public IList<BaseItem> GetRecursiveChildren(bool includeLinkedChildren)
+        public IReadOnlyList<BaseItem> GetRecursiveChildren(bool includeLinkedChildren)
         {
             return GetRecursiveChildren(i => true, includeLinkedChildren);
         }
 
-        public IList<BaseItem> GetRecursiveChildren(Func<BaseItem, bool> filter)
+        public IReadOnlyList<BaseItem> GetRecursiveChildren(Func<BaseItem, bool> filter)
         {
             return GetRecursiveChildren(filter, true);
         }
 
-        public IList<BaseItem> GetRecursiveChildren(Func<BaseItem, bool> filter, bool includeLinkedChildren)
+        public IReadOnlyList<BaseItem> GetRecursiveChildren(Func<BaseItem, bool> filter, bool includeLinkedChildren)
         {
             var result = new Dictionary<Guid, BaseItem>();
 
             AddChildrenToList(result, includeLinkedChildren, true, filter);
 
-            return result.Values.ToList();
+            return result.Values.ToImmutableList();
         }
 
         /// <summary>
@@ -1556,11 +1558,12 @@ namespace MediaBrowser.Controller.Entities
         /// Gets the linked children.
         /// </summary>
         /// <returns>IEnumerable{BaseItem}.</returns>
-        public IEnumerable<Tuple<LinkedChild, BaseItem>> GetLinkedChildrenInfos()
+        public IReadOnlyList<Tuple<LinkedChild, BaseItem>> GetLinkedChildrenInfos()
         {
             return LinkedChildren
                 .Select(i => new Tuple<LinkedChild, BaseItem>(i, GetLinkedChild(i)))
-                .Where(i => i.Item2 is not null);
+                .Where(i => i.Item2 is not null)
+                .ToImmutableList();
         }
 
         protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, IReadOnlyList<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)

+ 1 - 1
MediaBrowser.Controller/Entities/Genre.cs

@@ -61,7 +61,7 @@ namespace MediaBrowser.Controller.Entities
             return false;
         }
 
-        public IList<BaseItem> GetTaggedItems(InternalItemsQuery query)
+        public IReadOnlyList<BaseItem> GetTaggedItems(InternalItemsQuery query)
         {
             query.GenreIds = new[] { Id };
             query.ExcludeItemTypes = new[]

+ 2 - 2
MediaBrowser.Controller/Entities/IHasMediaSources.cs

@@ -22,8 +22,8 @@ namespace MediaBrowser.Controller.Entities
         /// </summary>
         /// <param name="enablePathSubstitution"><c>true</c> to enable path substitution, <c>false</c> to not.</param>
         /// <returns>A list of media sources.</returns>
-        List<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution);
+        IReadOnlyList<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution);
 
-        List<MediaStream> GetMediaStreams();
+        IReadOnlyList<MediaStream> GetMediaStreams();
     }
 }

+ 1 - 1
MediaBrowser.Controller/Entities/IItemByName.cs

@@ -9,7 +9,7 @@ namespace MediaBrowser.Controller.Entities
     /// </summary>
     public interface IItemByName
     {
-        IList<BaseItem> GetTaggedItems(InternalItemsQuery query);
+        IReadOnlyList<BaseItem> GetTaggedItems(InternalItemsQuery query);
     }
 
     public interface IHasDualAccess : IItemByName

+ 7 - 6
MediaBrowser.Controller/Entities/Movies/BoxSet.cs

@@ -4,6 +4,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Collections.Immutable;
 using System.Linq;
 using System.Text.Json.Serialization;
 using Jellyfin.Data.Entities;
@@ -91,7 +92,7 @@ namespace MediaBrowser.Controller.Entities.Movies
             return Enumerable.Empty<BaseItem>();
         }
 
-        protected override List<BaseItem> LoadChildren()
+        protected override IReadOnlyList<BaseItem> LoadChildren()
         {
             if (IsLegacyBoxSet)
             {
@@ -99,7 +100,7 @@ namespace MediaBrowser.Controller.Entities.Movies
             }
 
             // Save a trip to the database
-            return new List<BaseItem>();
+            return [];
         }
 
         public override bool IsAuthorizedToDelete(User user, List<Folder> allCollectionFolders)
@@ -127,16 +128,16 @@ namespace MediaBrowser.Controller.Entities.Movies
             return LibraryManager.Sort(items, user, new[] { sortBy }, SortOrder.Ascending);
         }
 
-        public override List<BaseItem> GetChildren(User user, bool includeLinkedChildren, InternalItemsQuery query)
+        public override IReadOnlyList<BaseItem> GetChildren(User user, bool includeLinkedChildren, InternalItemsQuery query)
         {
             var children = base.GetChildren(user, includeLinkedChildren, query);
-            return Sort(children, user).ToList();
+            return Sort(children, user).ToImmutableList();
         }
 
-        public override IEnumerable<BaseItem> GetRecursiveChildren(User user, InternalItemsQuery query)
+        public override IReadOnlyList<BaseItem> GetRecursiveChildren(User user, InternalItemsQuery query)
         {
             var children = base.GetRecursiveChildren(user, query);
-            return Sort(children, user).ToList();
+            return Sort(children, user).ToImmutableList();
         }
 
         public BoxSetInfo GetLookupInfo()

+ 1 - 1
MediaBrowser.Controller/Entities/PeopleHelper.cs

@@ -10,7 +10,7 @@ namespace MediaBrowser.Controller.Entities
 {
     public static class PeopleHelper
     {
-        public static void AddPerson(List<PersonInfo> people, PersonInfo person)
+        public static void AddPerson(ICollection<PersonInfo> people, PersonInfo person)
         {
             ArgumentNullException.ThrowIfNull(person);
             ArgumentException.ThrowIfNullOrEmpty(person.Name);

+ 1 - 1
MediaBrowser.Controller/Entities/Person.cs

@@ -62,7 +62,7 @@ namespace MediaBrowser.Controller.Entities
             return value;
         }
 
-        public IList<BaseItem> GetTaggedItems(InternalItemsQuery query)
+        public IReadOnlyList<BaseItem> GetTaggedItems(InternalItemsQuery query)
         {
             query.PersonIds = new[] { Id };
 

+ 1 - 1
MediaBrowser.Controller/Entities/Studio.cs

@@ -63,7 +63,7 @@ namespace MediaBrowser.Controller.Entities
             return true;
         }
 
-        public IList<BaseItem> GetTaggedItems(InternalItemsQuery query)
+        public IReadOnlyList<BaseItem> GetTaggedItems(InternalItemsQuery query)
         {
             query.StudioIds = new[] { Id };
 

+ 2 - 2
MediaBrowser.Controller/Entities/TV/Series.cs

@@ -189,12 +189,12 @@ namespace MediaBrowser.Controller.Entities.TV
             return list;
         }
 
-        public override List<BaseItem> GetChildren(User user, bool includeLinkedChildren, InternalItemsQuery query)
+        public override IReadOnlyList<BaseItem> GetChildren(User user, bool includeLinkedChildren, InternalItemsQuery query)
         {
             return GetSeasons(user, new DtoOptions(true));
         }
 
-        public List<BaseItem> GetSeasons(User user, DtoOptions options)
+        public IReadOnlyList<BaseItem> GetSeasons(User user, DtoOptions options)
         {
             var query = new InternalItemsQuery(user)
             {

+ 1 - 1
MediaBrowser.Controller/Entities/UserRootFolder.cs

@@ -52,7 +52,7 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
-        protected override List<BaseItem> LoadChildren()
+        protected override IReadOnlyList<BaseItem> LoadChildren()
         {
             lock (_childIdsLock)
             {

+ 2 - 2
MediaBrowser.Controller/Entities/UserView.cs

@@ -134,7 +134,7 @@ namespace MediaBrowser.Controller.Entities
         }
 
         /// <inheritdoc />
-        public override IEnumerable<BaseItem> GetRecursiveChildren(User user, InternalItemsQuery query)
+        public override IReadOnlyList<BaseItem> GetRecursiveChildren(User user, InternalItemsQuery query)
         {
             query.SetUser(user);
             query.Recursive = true;
@@ -145,7 +145,7 @@ namespace MediaBrowser.Controller.Entities
         }
 
         /// <inheritdoc />
-        protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
+        protected override IReadOnlyList<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
         {
             return GetChildren(user, false);
         }

+ 1 - 1
MediaBrowser.Controller/Entities/UserViewBuilder.cs

@@ -236,7 +236,7 @@ namespace MediaBrowser.Controller.Entities
             return ConvertToResult(_libraryManager.GetItemList(query));
         }
 
-        private QueryResult<BaseItem> ConvertToResult(List<BaseItem> items)
+        private QueryResult<BaseItem> ConvertToResult(IReadOnlyList<BaseItem> items)
         {
             return new QueryResult<BaseItem>(items);
         }

+ 1 - 1
MediaBrowser.Controller/Entities/Year.cs

@@ -55,7 +55,7 @@ namespace MediaBrowser.Controller.Entities
             return true;
         }
 
-        public IList<BaseItem> GetTaggedItems(InternalItemsQuery query)
+        public IReadOnlyList<BaseItem> GetTaggedItems(InternalItemsQuery query)
         {
             if (!int.TryParse(Name, NumberStyles.Integer, CultureInfo.InvariantCulture, out var year))
             {

+ 9 - 9
MediaBrowser.Controller/Library/ILibraryManager.cs

@@ -483,21 +483,21 @@ namespace MediaBrowser.Controller.Library
         /// </summary>
         /// <param name="item">The item.</param>
         /// <returns>List&lt;PersonInfo&gt;.</returns>
-        List<PersonInfo> GetPeople(BaseItem item);
+        IReadOnlyList<PersonInfo> GetPeople(BaseItem item);
 
         /// <summary>
         /// Gets the people.
         /// </summary>
         /// <param name="query">The query.</param>
         /// <returns>List&lt;PersonInfo&gt;.</returns>
-        List<PersonInfo> GetPeople(InternalPeopleQuery query);
+        IReadOnlyList<PersonInfo> GetPeople(InternalPeopleQuery query);
 
         /// <summary>
         /// Gets the people items.
         /// </summary>
         /// <param name="query">The query.</param>
         /// <returns>List&lt;Person&gt;.</returns>
-        List<Person> GetPeopleItems(InternalPeopleQuery query);
+        IReadOnlyList<Person> GetPeopleItems(InternalPeopleQuery query);
 
         /// <summary>
         /// Updates the people.
@@ -513,21 +513,21 @@ namespace MediaBrowser.Controller.Library
         /// <param name="people">The people.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>The async task.</returns>
-        Task UpdatePeopleAsync(BaseItem item, List<PersonInfo> people, CancellationToken cancellationToken);
+        Task UpdatePeopleAsync(BaseItem item, IReadOnlyList<PersonInfo> people, CancellationToken cancellationToken);
 
         /// <summary>
         /// Gets the item ids.
         /// </summary>
         /// <param name="query">The query.</param>
         /// <returns>List&lt;Guid&gt;.</returns>
-        List<Guid> GetItemIds(InternalItemsQuery query);
+        IReadOnlyList<Guid> GetItemIds(InternalItemsQuery query);
 
         /// <summary>
         /// Gets the people names.
         /// </summary>
         /// <param name="query">The query.</param>
         /// <returns>List&lt;System.String&gt;.</returns>
-        List<string> GetPeopleNames(InternalPeopleQuery query);
+        IReadOnlyList<string> GetPeopleNames(InternalPeopleQuery query);
 
         /// <summary>
         /// Queries the items.
@@ -553,9 +553,9 @@ namespace MediaBrowser.Controller.Library
         /// </summary>
         /// <param name="query">The query.</param>
         /// <returns>QueryResult&lt;BaseItem&gt;.</returns>
-        List<BaseItem> GetItemList(InternalItemsQuery query);
+        IReadOnlyList<BaseItem> GetItemList(InternalItemsQuery query);
 
-        List<BaseItem> GetItemList(InternalItemsQuery query, bool allowExternalContent);
+        IReadOnlyList<BaseItem> GetItemList(InternalItemsQuery query, bool allowExternalContent);
 
         /// <summary>
         /// Gets the items.
@@ -563,7 +563,7 @@ namespace MediaBrowser.Controller.Library
         /// <param name="query">The query to use.</param>
         /// <param name="parents">Items to use for query.</param>
         /// <returns>List of items.</returns>
-        List<BaseItem> GetItemList(InternalItemsQuery query, List<BaseItem> parents);
+        IReadOnlyList<BaseItem> GetItemList(InternalItemsQuery query, List<BaseItem> parents);
 
         /// <summary>
         /// Gets the items result.

+ 4 - 4
MediaBrowser.Controller/Library/IMediaSourceManager.cs

@@ -29,28 +29,28 @@ namespace MediaBrowser.Controller.Library
         /// </summary>
         /// <param name="itemId">The item identifier.</param>
         /// <returns>IEnumerable&lt;MediaStream&gt;.</returns>
-        List<MediaStream> GetMediaStreams(Guid itemId);
+        IReadOnlyList<MediaStream> GetMediaStreams(Guid itemId);
 
         /// <summary>
         /// Gets the media streams.
         /// </summary>
         /// <param name="query">The query.</param>
         /// <returns>IEnumerable&lt;MediaStream&gt;.</returns>
-        List<MediaStream> GetMediaStreams(MediaStreamQuery query);
+        IReadOnlyList<MediaStream> GetMediaStreams(MediaStreamQuery query);
 
         /// <summary>
         /// Gets the media attachments.
         /// </summary>
         /// <param name="itemId">The item identifier.</param>
         /// <returns>IEnumerable&lt;MediaAttachment&gt;.</returns>
-        List<MediaAttachment> GetMediaAttachments(Guid itemId);
+        IReadOnlyList<MediaAttachment> GetMediaAttachments(Guid itemId);
 
         /// <summary>
         /// Gets the media attachments.
         /// </summary>
         /// <param name="query">The query.</param>
         /// <returns>IEnumerable&lt;MediaAttachment&gt;.</returns>
-        List<MediaAttachment> GetMediaAttachments(MediaAttachmentQuery query);
+        IReadOnlyList<MediaAttachment> GetMediaAttachments(MediaAttachmentQuery query);
 
         /// <summary>
         /// Gets the playack media sources.

+ 3 - 3
MediaBrowser.Controller/Library/IMusicManager.cs

@@ -17,7 +17,7 @@ namespace MediaBrowser.Controller.Library
         /// <param name="user">The user to use.</param>
         /// <param name="dtoOptions">The options to use.</param>
         /// <returns>List of items.</returns>
-        List<BaseItem> GetInstantMixFromItem(BaseItem item, User? user, DtoOptions dtoOptions);
+        IReadOnlyList<BaseItem> GetInstantMixFromItem(BaseItem item, User? user, DtoOptions dtoOptions);
 
         /// <summary>
         /// Gets the instant mix from artist.
@@ -26,7 +26,7 @@ namespace MediaBrowser.Controller.Library
         /// <param name="user">The user to use.</param>
         /// <param name="dtoOptions">The options to use.</param>
         /// <returns>List of items.</returns>
-        List<BaseItem> GetInstantMixFromArtist(MusicArtist artist, User? user, DtoOptions dtoOptions);
+        IReadOnlyList<BaseItem> GetInstantMixFromArtist(MusicArtist artist, User? user, DtoOptions dtoOptions);
 
         /// <summary>
         /// Gets the instant mix from genre.
@@ -35,6 +35,6 @@ namespace MediaBrowser.Controller.Library
         /// <param name="user">The user to use.</param>
         /// <param name="dtoOptions">The options to use.</param>
         /// <returns>List of items.</returns>
-        List<BaseItem> GetInstantMixFromGenres(IEnumerable<string> genres, User? user, DtoOptions dtoOptions);
+        IReadOnlyList<BaseItem> GetInstantMixFromGenres(IEnumerable<string> genres, User? user, DtoOptions dtoOptions);
     }
 }

+ 5 - 4
MediaBrowser.Controller/LiveTv/LiveTvChannel.cs

@@ -4,6 +4,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Collections.Immutable;
 using System.Globalization;
 using System.Linq;
 using System.Text.Json.Serialization;
@@ -122,7 +123,7 @@ namespace MediaBrowser.Controller.LiveTv
         public IEnumerable<BaseItem> GetTaggedItems()
             => Enumerable.Empty<BaseItem>();
 
-        public override List<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
+        public override IReadOnlyList<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution)
         {
             var list = new List<MediaSourceInfo>();
 
@@ -140,12 +141,12 @@ namespace MediaBrowser.Controller.LiveTv
 
             list.Add(info);
 
-            return list;
+            return list.ToImmutableList();
         }
 
-        public override List<MediaStream> GetMediaStreams()
+        public override IReadOnlyList<MediaStream> GetMediaStreams()
         {
-            return new List<MediaStream>();
+            return [];
         }
 
         protected override string GetInternalMetadataPath(string basePath)

+ 5 - 5
MediaBrowser.Controller/Playlists/Playlist.cs

@@ -137,27 +137,27 @@ namespace MediaBrowser.Controller.Playlists
             return Task.CompletedTask;
         }
 
-        public override List<BaseItem> GetChildren(User user, bool includeLinkedChildren, InternalItemsQuery query)
+        public override IReadOnlyList<BaseItem> GetChildren(User user, bool includeLinkedChildren, InternalItemsQuery query)
         {
             return GetPlayableItems(user, query);
         }
 
-        protected override IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
+        protected override IReadOnlyList<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
         {
             return [];
         }
 
-        public override IEnumerable<BaseItem> GetRecursiveChildren(User user, InternalItemsQuery query)
+        public override IReadOnlyList<BaseItem> GetRecursiveChildren(User user, InternalItemsQuery query)
         {
             return GetPlayableItems(user, query);
         }
 
-        public IEnumerable<Tuple<LinkedChild, BaseItem>> GetManageableItems()
+        public IReadOnlyList<Tuple<LinkedChild, BaseItem>> GetManageableItems()
         {
             return GetLinkedChildrenInfos();
         }
 
-        private List<BaseItem> GetPlayableItems(User user, InternalItemsQuery query)
+        private IReadOnlyList<BaseItem> GetPlayableItems(User user, InternalItemsQuery query)
         {
             query ??= new InternalItemsQuery(user);
 

+ 11 - 5
MediaBrowser.Controller/Providers/MetadataResult.cs

@@ -3,6 +3,7 @@
 #pragma warning disable CA1002, CA2227, CS1591
 
 using System.Collections.Generic;
+using System.Linq;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Model.Entities;
 
@@ -13,6 +14,7 @@ namespace MediaBrowser.Controller.Providers
         // Images aren't always used so the allocation is a waste a lot of the time
         private List<LocalImageInfo> _images;
         private List<(string Url, ImageType Type)> _remoteImages;
+        private List<PersonInfo> _people;
 
         public MetadataResult()
         {
@@ -21,17 +23,21 @@ namespace MediaBrowser.Controller.Providers
 
         public List<LocalImageInfo> Images
         {
-            get => _images ??= new List<LocalImageInfo>();
+            get => _images ??= [];
             set => _images = value;
         }
 
         public List<(string Url, ImageType Type)> RemoteImages
         {
-            get => _remoteImages ??= new List<(string Url, ImageType Type)>();
+            get => _remoteImages ??= [];
             set => _remoteImages = value;
         }
 
-        public List<PersonInfo> People { get; set; }
+        public IReadOnlyList<PersonInfo> People
+        {
+            get => _people;
+            set => _people = value.ToList();
+        }
 
         public bool HasMetadata { get; set; }
 
@@ -47,7 +53,7 @@ namespace MediaBrowser.Controller.Providers
         {
             People ??= new List<PersonInfo>();
 
-            PeopleHelper.AddPerson(People, p);
+            PeopleHelper.AddPerson(_people, p);
         }
 
         /// <summary>
@@ -61,7 +67,7 @@ namespace MediaBrowser.Controller.Providers
             }
             else
             {
-                People.Clear();
+                _people.Clear();
             }
         }
     }

+ 1 - 1
MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs

@@ -39,7 +39,7 @@ namespace MediaBrowser.Providers.BoxSets
         protected override bool EnableUpdatingPremiereDateFromChildren => true;
 
         /// <inheritdoc />
-        protected override IList<BaseItem> GetChildrenForMetadataUpdates(BoxSet item)
+        protected override IReadOnlyList<BaseItem> GetChildrenForMetadataUpdates(BoxSet item)
         {
             return item.GetLinkedChildren();
         }

+ 10 - 15
MediaBrowser.Providers/Manager/MetadataService.cs

@@ -322,17 +322,17 @@ namespace MediaBrowser.Providers.Manager
             return false;
         }
 
-        protected virtual IList<BaseItem> GetChildrenForMetadataUpdates(TItemType item)
+        protected virtual IReadOnlyList<BaseItem> GetChildrenForMetadataUpdates(TItemType item)
         {
             if (item is Folder folder)
             {
                 return folder.GetRecursiveChildren();
             }
 
-            return Array.Empty<BaseItem>();
+            return [];
         }
 
-        protected virtual ItemUpdateType UpdateMetadataFromChildren(TItemType item, IList<BaseItem> children, bool isFullRefresh, ItemUpdateType currentUpdateType)
+        protected virtual ItemUpdateType UpdateMetadataFromChildren(TItemType item, IReadOnlyList<BaseItem> children, bool isFullRefresh, ItemUpdateType currentUpdateType)
         {
             var updateType = ItemUpdateType.None;
 
@@ -371,7 +371,7 @@ namespace MediaBrowser.Providers.Manager
             return updateType;
         }
 
-        private ItemUpdateType UpdateCumulativeRunTimeTicks(TItemType item, IList<BaseItem> children)
+        private ItemUpdateType UpdateCumulativeRunTimeTicks(TItemType item, IReadOnlyList<BaseItem> children)
         {
             if (item is Folder folder && folder.SupportsCumulativeRunTimeTicks)
             {
@@ -395,7 +395,7 @@ namespace MediaBrowser.Providers.Manager
             return ItemUpdateType.None;
         }
 
-        private ItemUpdateType UpdateDateLastMediaAdded(TItemType item, IList<BaseItem> children)
+        private ItemUpdateType UpdateDateLastMediaAdded(TItemType item, IReadOnlyList<BaseItem> children)
         {
             var updateType = ItemUpdateType.None;
 
@@ -429,7 +429,7 @@ namespace MediaBrowser.Providers.Manager
             return updateType;
         }
 
-        private ItemUpdateType UpdatePremiereDate(TItemType item, IList<BaseItem> children)
+        private ItemUpdateType UpdatePremiereDate(TItemType item, IReadOnlyList<BaseItem> children)
         {
             var updateType = ItemUpdateType.None;
 
@@ -467,7 +467,7 @@ namespace MediaBrowser.Providers.Manager
             return updateType;
         }
 
-        private ItemUpdateType UpdateGenres(TItemType item, IList<BaseItem> children)
+        private ItemUpdateType UpdateGenres(TItemType item, IReadOnlyList<BaseItem> children)
         {
             var updateType = ItemUpdateType.None;
 
@@ -488,7 +488,7 @@ namespace MediaBrowser.Providers.Manager
             return updateType;
         }
 
-        private ItemUpdateType UpdateStudios(TItemType item, IList<BaseItem> children)
+        private ItemUpdateType UpdateStudios(TItemType item, IReadOnlyList<BaseItem> children)
         {
             var updateType = ItemUpdateType.None;
 
@@ -509,7 +509,7 @@ namespace MediaBrowser.Providers.Manager
             return updateType;
         }
 
-        private ItemUpdateType UpdateOfficialRating(TItemType item, IList<BaseItem> children)
+        private ItemUpdateType UpdateOfficialRating(TItemType item, IReadOnlyList<BaseItem> children)
         {
             var updateType = ItemUpdateType.None;
 
@@ -1142,13 +1142,8 @@ namespace MediaBrowser.Providers.Manager
             }
         }
 
-        private static void MergePeople(List<PersonInfo> source, List<PersonInfo> target)
+        private static void MergePeople(IReadOnlyList<PersonInfo> source, IReadOnlyList<PersonInfo> target)
         {
-            if (target is null)
-            {
-                target = new List<PersonInfo>();
-            }
-
             foreach (var person in target)
             {
                 var normalizedName = person.Name.RemoveDiacritics();

+ 6 - 2
MediaBrowser.Providers/MediaInfo/AudioFileProber.cs

@@ -36,6 +36,7 @@ namespace MediaBrowser.Providers.MediaInfo
         private readonly IMediaSourceManager _mediaSourceManager;
         private readonly LyricResolver _lyricResolver;
         private readonly ILyricManager _lyricManager;
+        private readonly IMediaStreamRepository _mediaStreamRepository;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="AudioFileProber"/> class.
@@ -47,6 +48,7 @@ namespace MediaBrowser.Providers.MediaInfo
         /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
         /// <param name="lyricResolver">Instance of the <see cref="LyricResolver"/> interface.</param>
         /// <param name="lyricManager">Instance of the <see cref="ILyricManager"/> interface.</param>
+        /// <param name="mediaStreamRepository">Instance of the <see cref="IMediaStreamRepository"/>.</param>
         public AudioFileProber(
             ILogger<AudioFileProber> logger,
             IMediaSourceManager mediaSourceManager,
@@ -54,7 +56,8 @@ namespace MediaBrowser.Providers.MediaInfo
             IItemRepository itemRepo,
             ILibraryManager libraryManager,
             LyricResolver lyricResolver,
-            ILyricManager lyricManager)
+            ILyricManager lyricManager,
+            IMediaStreamRepository mediaStreamRepository)
         {
             _mediaEncoder = mediaEncoder;
             _itemRepo = itemRepo;
@@ -63,6 +66,7 @@ namespace MediaBrowser.Providers.MediaInfo
             _mediaSourceManager = mediaSourceManager;
             _lyricResolver = lyricResolver;
             _lyricManager = lyricManager;
+            _mediaStreamRepository = mediaStreamRepository;
             ATL.Settings.DisplayValueSeparator = InternalValueSeparator;
             ATL.Settings.UseFileNameWhenNoTitle = false;
             ATL.Settings.ID3v2_separatev2v3Values = false;
@@ -149,7 +153,7 @@ namespace MediaBrowser.Providers.MediaInfo
 
             audio.HasLyrics = mediaStreams.Any(s => s.Type == MediaStreamType.Lyric);
 
-            _itemRepo.SaveMediaStreams(audio.Id, mediaStreams, cancellationToken);
+            _mediaStreamRepository.SaveMediaStreams(audio.Id, mediaStreams, cancellationToken);
         }
 
         /// <summary>

+ 1 - 1
MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs

@@ -74,7 +74,7 @@ namespace MediaBrowser.Providers.MediaInfo
             return GetImage((Audio)item, imageStreams, cancellationToken);
         }
 
-        private async Task<DynamicImageResponse> GetImage(Audio item, List<MediaStream> imageStreams, CancellationToken cancellationToken)
+        private async Task<DynamicImageResponse> GetImage(Audio item, IReadOnlyList<MediaStream> imageStreams, CancellationToken cancellationToken)
         {
             var path = GetAudioImagePath(item);
 

+ 11 - 4
MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs

@@ -31,6 +31,7 @@ namespace MediaBrowser.Providers.MediaInfo
     public class FFProbeVideoInfo
     {
         private readonly ILogger<FFProbeVideoInfo> _logger;
+        private readonly IMediaSourceManager _mediaSourceManager;
         private readonly IMediaEncoder _mediaEncoder;
         private readonly IItemRepository _itemRepo;
         private readonly IBlurayExaminer _blurayExaminer;
@@ -42,7 +43,8 @@ namespace MediaBrowser.Providers.MediaInfo
         private readonly ILibraryManager _libraryManager;
         private readonly AudioResolver _audioResolver;
         private readonly SubtitleResolver _subtitleResolver;
-        private readonly IMediaSourceManager _mediaSourceManager;
+        private readonly IMediaAttachmentRepository _mediaAttachmentRepository;
+        private readonly IMediaStreamRepository _mediaStreamRepository;
 
         public FFProbeVideoInfo(
             ILogger<FFProbeVideoInfo> logger,
@@ -57,7 +59,9 @@ namespace MediaBrowser.Providers.MediaInfo
             IChapterRepository chapterManager,
             ILibraryManager libraryManager,
             AudioResolver audioResolver,
-            SubtitleResolver subtitleResolver)
+            SubtitleResolver subtitleResolver,
+            IMediaAttachmentRepository mediaAttachmentRepository,
+            IMediaStreamRepository mediaStreamRepository)
         {
             _logger = logger;
             _mediaSourceManager = mediaSourceManager;
@@ -72,6 +76,9 @@ namespace MediaBrowser.Providers.MediaInfo
             _libraryManager = libraryManager;
             _audioResolver = audioResolver;
             _subtitleResolver = subtitleResolver;
+            _mediaAttachmentRepository = mediaAttachmentRepository;
+            _mediaStreamRepository = mediaStreamRepository;
+            _mediaStreamRepository = mediaStreamRepository;
         }
 
         public async Task<ItemUpdateType> ProbeVideo<T>(
@@ -267,11 +274,11 @@ namespace MediaBrowser.Providers.MediaInfo
 
             video.HasSubtitles = mediaStreams.Any(i => i.Type == MediaStreamType.Subtitle);
 
-            _itemRepo.SaveMediaStreams(video.Id, mediaStreams, cancellationToken);
+            _mediaStreamRepository.SaveMediaStreams(video.Id, mediaStreams, cancellationToken);
 
             if (mediaAttachments.Any())
             {
-                _itemRepo.SaveMediaAttachments(video.Id, mediaAttachments, cancellationToken);
+                _mediaAttachmentRepository.SaveMediaAttachments(video.Id, mediaAttachments, cancellationToken);
             }
 
             if (options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh

+ 10 - 3
MediaBrowser.Providers/MediaInfo/ProbeProvider.cs

@@ -67,6 +67,8 @@ namespace MediaBrowser.Providers.MediaInfo
         /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
         /// <param name="namingOptions">The <see cref="NamingOptions"/>.</param>
         /// <param name="lyricManager">Instance of the <see cref="ILyricManager"/> interface.</param>
+        /// <param name="mediaAttachmentRepository">Instance of the <see cref="IMediaAttachmentRepository"/> interface.</param>
+        /// <param name="mediaStreamRepository">Instance of the <see cref="IMediaStreamRepository"/> interface.</param>
         public ProbeProvider(
             IMediaSourceManager mediaSourceManager,
             IMediaEncoder mediaEncoder,
@@ -81,7 +83,9 @@ namespace MediaBrowser.Providers.MediaInfo
             IFileSystem fileSystem,
             ILoggerFactory loggerFactory,
             NamingOptions namingOptions,
-            ILyricManager lyricManager)
+            ILyricManager lyricManager,
+            IMediaAttachmentRepository mediaAttachmentRepository,
+            IMediaStreamRepository mediaStreamRepository)
         {
             _logger = loggerFactory.CreateLogger<ProbeProvider>();
             _audioResolver = new AudioResolver(loggerFactory.CreateLogger<AudioResolver>(), localization, mediaEncoder, fileSystem, namingOptions);
@@ -101,7 +105,9 @@ namespace MediaBrowser.Providers.MediaInfo
                 chapterManager,
                 libraryManager,
                 _audioResolver,
-                _subtitleResolver);
+                _subtitleResolver,
+                mediaAttachmentRepository,
+                mediaStreamRepository);
 
             _audioProber = new AudioFileProber(
                 loggerFactory.CreateLogger<AudioFileProber>(),
@@ -110,7 +116,8 @@ namespace MediaBrowser.Providers.MediaInfo
                 itemRepo,
                 libraryManager,
                 _lyricResolver,
-                lyricManager);
+                lyricManager,
+                mediaStreamRepository);
         }
 
         /// <inheritdoc />

+ 3 - 3
MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs

@@ -31,7 +31,7 @@ namespace MediaBrowser.Providers.MediaInfo
 
         public async Task<List<string>> DownloadSubtitles(
             Video video,
-            List<MediaStream> mediaStreams,
+            IReadOnlyList<MediaStream> mediaStreams,
             bool skipIfEmbeddedSubtitlesPresent,
             bool skipIfAudioTrackMatches,
             bool requirePerfectMatch,
@@ -68,7 +68,7 @@ namespace MediaBrowser.Providers.MediaInfo
 
         public Task<bool> DownloadSubtitles(
             Video video,
-            List<MediaStream> mediaStreams,
+            IReadOnlyList<MediaStream> mediaStreams,
             bool skipIfEmbeddedSubtitlesPresent,
             bool skipIfAudioTrackMatches,
             bool requirePerfectMatch,
@@ -120,7 +120,7 @@ namespace MediaBrowser.Providers.MediaInfo
 
         private async Task<bool> DownloadSubtitles(
             Video video,
-            List<MediaStream> mediaStreams,
+            IReadOnlyList<MediaStream> mediaStreams,
             bool skipIfEmbeddedSubtitlesPresent,
             bool skipIfAudioTrackMatches,
             bool requirePerfectMatch,

+ 2 - 2
MediaBrowser.Providers/Music/AlbumMetadataService.cs

@@ -47,11 +47,11 @@ namespace MediaBrowser.Providers.Music
         protected override bool EnableUpdatingStudiosFromChildren => true;
 
         /// <inheritdoc />
-        protected override IList<BaseItem> GetChildrenForMetadataUpdates(MusicAlbum item)
+        protected override IReadOnlyList<BaseItem> GetChildrenForMetadataUpdates(MusicAlbum item)
             => item.GetRecursiveChildren(i => i is Audio);
 
         /// <inheritdoc />
-        protected override ItemUpdateType UpdateMetadataFromChildren(MusicAlbum item, IList<BaseItem> children, bool isFullRefresh, ItemUpdateType currentUpdateType)
+        protected override ItemUpdateType UpdateMetadataFromChildren(MusicAlbum item, IReadOnlyList<BaseItem> children, bool isFullRefresh, ItemUpdateType currentUpdateType)
         {
             var updateType = base.UpdateMetadataFromChildren(item, children, isFullRefresh, currentUpdateType);
 

+ 3 - 2
MediaBrowser.Providers/Music/ArtistMetadataService.cs

@@ -1,6 +1,7 @@
 #pragma warning disable CS1591
 
 using System.Collections.Generic;
+using System.Collections.Immutable;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
@@ -28,7 +29,7 @@ namespace MediaBrowser.Providers.Music
         protected override bool EnableUpdatingGenresFromChildren => true;
 
         /// <inheritdoc />
-        protected override IList<BaseItem> GetChildrenForMetadataUpdates(MusicArtist item)
+        protected override IReadOnlyList<BaseItem> GetChildrenForMetadataUpdates(MusicArtist item)
         {
             return item.IsAccessedByName
                 ? item.GetTaggedItems(new InternalItemsQuery
@@ -36,7 +37,7 @@ namespace MediaBrowser.Providers.Music
                     Recursive = true,
                     IsFolder = false
                 })
-                : item.GetRecursiveChildren(i => i is IHasArtist && !i.IsFolder);
+                : item.GetRecursiveChildren(i => i is IHasArtist && !i.IsFolder).ToImmutableArray();
         }
     }
 }

+ 1 - 1
MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs

@@ -36,7 +36,7 @@ namespace MediaBrowser.Providers.Playlists
         protected override bool EnableUpdatingStudiosFromChildren => true;
 
         /// <inheritdoc />
-        protected override IList<BaseItem> GetChildrenForMetadataUpdates(Playlist item)
+        protected override IReadOnlyList<BaseItem> GetChildrenForMetadataUpdates(Playlist item)
             => item.GetLinkedChildren();
 
         /// <inheritdoc />

+ 3 - 3
MediaBrowser.Providers/TV/SeasonMetadataService.cs

@@ -80,11 +80,11 @@ namespace MediaBrowser.Providers.TV
         }
 
         /// <inheritdoc />
-        protected override IList<BaseItem> GetChildrenForMetadataUpdates(Season item)
+        protected override IReadOnlyList<BaseItem> GetChildrenForMetadataUpdates(Season item)
             => item.GetEpisodes();
 
         /// <inheritdoc />
-        protected override ItemUpdateType UpdateMetadataFromChildren(Season item, IList<BaseItem> children, bool isFullRefresh, ItemUpdateType currentUpdateType)
+        protected override ItemUpdateType UpdateMetadataFromChildren(Season item, IReadOnlyList<BaseItem> children, bool isFullRefresh, ItemUpdateType currentUpdateType)
         {
             var updateType = base.UpdateMetadataFromChildren(item, children, isFullRefresh, currentUpdateType);
 
@@ -96,7 +96,7 @@ namespace MediaBrowser.Providers.TV
             return updateType;
         }
 
-        private ItemUpdateType SaveIsVirtualItem(Season item, IList<BaseItem> episodes)
+        private ItemUpdateType SaveIsVirtualItem(Season item, IReadOnlyList<BaseItem> episodes)
         {
             var isVirtualItem = item.LocationType == LocationType.Virtual && (episodes.Count == 0 || episodes.All(i => i.LocationType == LocationType.Virtual));
 

+ 1 - 1
MediaBrowser.XbmcMetadata/Savers/ArtistNfoSaver.cs

@@ -67,7 +67,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
             AddAlbums(albums, writer);
         }
 
-        private void AddAlbums(IList<BaseItem> albums, XmlWriter writer)
+        private void AddAlbums(IReadOnlyList<BaseItem> albums, XmlWriter writer)
         {
             foreach (var album in albums)
             {

+ 1 - 1
MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs

@@ -914,7 +914,7 @@ namespace MediaBrowser.XbmcMetadata.Savers
             writer.WriteEndElement();
         }
 
-        private void AddActors(List<PersonInfo> people, XmlWriter writer, ILibraryManager libraryManager, bool saveImagePath)
+        private void AddActors(IReadOnlyList<PersonInfo> people, XmlWriter writer, ILibraryManager libraryManager, bool saveImagePath)
         {
             foreach (var person in people)
             {